Gemmi C++ API
Loading...
Searching...
No Matches
symmetry.hpp
Go to the documentation of this file.
1// Copyright 2017-2019 Global Phasing Ltd.
2//
3// Crystallographic Symmetry. Space Groups. Coordinate Triplets.
4//
5// If this is all that you need from Gemmi you can just copy this file,
6// fail.hpp and LICENSE.txt to your project.
7
8#ifndef GEMMI_SYMMETRY_HPP_
9#define GEMMI_SYMMETRY_HPP_
10
11#include <cstdlib> // for strtol, abs
12#include <cstring> // for memchr, strchr
13#include <cmath> // for fabs
14#include <array>
15#include <algorithm> // for count, sort, remove
16#include <functional> // for hash
17#include <stdexcept> // for runtime_error, invalid_argument
18#include <string>
19#include <tuple> // for tie
20#include <vector>
21
22#include "fail.hpp" // for fail, unreachable
23
24// we use brace elision with std:array's
25#ifdef __clang__
26# pragma clang diagnostic push
27# pragma clang diagnostic ignored "-Wmissing-braces"
28#endif
29
30namespace gemmi {
31
32// UTILS
33
34namespace impl {
35
36// copied a helper function from atox.hpp to keep it a two-header lib
37inline const char* skip_blank(const char* p) {
38 if (p)
39 while (*p == ' ' || *p == '\t' || *p == '_') // '_' can be used as space
40 ++p;
41 return p;
42}
43
44} // namespace impl
45
46
47// OP
48
49// Op is a symmetry operation, or a change-of-basic transformation,
50// or a different operation of similar kind.
51// Both "rotation" matrix and translation vector are fractional, with DEN
52// used as the denominator.
53struct Op {
54 static constexpr int DEN = 24; // 24 to handle 1/8 in change-of-basis
55 typedef std::array<std::array<int, 3>, 3> Rot;
56 typedef std::array<int, 3> Tran;
57
60
61 std::string triplet(char style='x') const;
62
63 Op inverse() const;
64
65 // If the translation points outside of the unit cell, wrap it.
66 Op& wrap() {
67 for (int i = 0; i != 3; ++i) {
68 if (tran[i] >= DEN) // elements need to be in [0,DEN)
69 tran[i] %= DEN;
70 else if (tran[i] < 0)
71 tran[i] = ((tran[i] + 1) % DEN) + DEN - 1;
72 }
73 return *this;
74 }
75
76 Op& translate(const Tran& a) {
77 for (int i = 0; i != 3; ++i)
78 tran[i] += a[i];
79 return *this;
80 }
81
82 Op translated(const Tran& a) const { return Op(*this).translate(a); }
83
84 Op add_centering(const Tran& a) const { return translated(a).wrap(); }
85
86 Rot negated_rot() const {
87 return { -rot[0][0], -rot[0][1], -rot[0][2],
88 -rot[1][0], -rot[1][1], -rot[1][2],
89 -rot[2][0], -rot[2][1], -rot[2][2] };
90 }
91
93 return { rot[0][0], rot[1][0], rot[2][0],
94 rot[0][1], rot[1][1], rot[2][1],
95 rot[0][2], rot[1][2], rot[2][2] };
96 }
97
98 // DEN^3 for rotation, -DEN^3 for rotoinversion
99 int det_rot() const {
100 return rot[0][0] * (rot[1][1] * rot[2][2] - rot[1][2] * rot[2][1])
101 - rot[0][1] * (rot[1][0] * rot[2][2] - rot[1][2] * rot[2][0])
102 + rot[0][2] * (rot[1][0] * rot[2][1] - rot[1][1] * rot[2][0]);
103 }
104
105 // Rotation-part type based on Table 1 in RWGK, Acta Cryst. A55, 383 (1999)
106 int rot_type() const {
107 int det = det_rot();
108 int tr_den = rot[0][0] + rot[1][1] + rot[2][2];
109 int tr = tr_den / DEN;
110 const int table[] = {0, 0, 2, 3, 4, 6, 1};
111 if (std::abs(det) == DEN * DEN * DEN && tr * DEN == tr_den && std::abs(tr) <= 3)
112 return det > 0 ? table[3 + tr] : -table[3 - tr];
113 return 0;
114 }
115
116 Op combine(const Op& b) const {
117 Op r;
118 for (int i = 0; i != 3; ++i) {
119 r.tran[i] = tran[i] * Op::DEN;
120 for (int j = 0; j != 3; ++j) {
121 r.rot[i][j] = (rot[i][0] * b.rot[0][j] +
122 rot[i][1] * b.rot[1][j] +
123 rot[i][2] * b.rot[2][j]) / Op::DEN;
124 r.tran[i] += rot[i][j] * b.tran[j];
125 }
126 r.tran[i] /= Op::DEN;
127 }
128 return r;
129 }
130
131 std::array<double, 3> apply_to_xyz(const std::array<double, 3>& xyz) const {
132 std::array<double, 3> out;
133 for (int i = 0; i != 3; ++i)
134 out[i] = (rot[i][0] * xyz[0] + rot[i][1] * xyz[1] + rot[i][2] * xyz[2] +
135 tran[i]) / Op::DEN;
136 return out;
137 }
138
139 // Miller is defined in the same way in namespace gemmi in unitcell.hpp
140 using Miller = std::array<int, 3>;
141
143 Miller r;
144 for (int i = 0; i != 3; ++i)
145 r[i] = (rot[0][i] * hkl[0] + rot[1][i] * hkl[1] + rot[2][i] * hkl[2]);
146 return r;
147 }
148 static Miller divide_hkl_by_DEN(const Miller& hkl) {
149 return {{ hkl[0] / DEN, hkl[1] / DEN, hkl[2] / DEN }};
150 }
151 Miller apply_to_hkl(const Miller& hkl) const {
153 }
154
155 double phase_shift(const Miller& hkl) const {
156 constexpr double mult = -2 * 3.1415926535897932384626433832795 / Op::DEN;
157 return mult * (hkl[0] * tran[0] + hkl[1] * tran[1] + hkl[2] * tran[2]);
158 }
159
160 std::array<std::array<int, 4>, 4> int_seitz() const {
161 std::array<std::array<int, 4>, 4> t;
162 for (int i = 0; i < 3; ++i)
163 t[i] = { rot[i][0], rot[i][1], rot[i][2], tran[i] };
164 t[3] = { 0, 0, 0, 1 };
165 return t;
166 }
167
168 std::array<std::array<double, 4>, 4> float_seitz() const {
169 std::array<std::array<double, 4>, 4> t;
170 double m = 1.0 / Op::DEN;
171 for (int i = 0; i < 3; ++i)
172 t[i] = { m * rot[i][0], m * rot[i][1], m * rot[i][2], m * tran[i] };
173 t[3] = { 0., 0., 0., 1. };
174 return t;
175 }
176
177 static constexpr Op identity() {
178 return {{DEN,0,0, 0,DEN,0, 0,0,DEN}, {0,0,0}};
179 }
180 static constexpr Op::Rot inversion_rot() {
181 return {-DEN,0,0, 0,-DEN,0, 0,0,-DEN};
182 }
183 bool operator<(const Op& rhs) const {
184 return std::tie(rot, tran) < std::tie(rhs.rot, rhs.tran);
185 }
186};
187
188inline bool operator==(const Op& a, const Op& b) {
189 return a.rot == b.rot && a.tran == b.tran;
190}
191inline bool operator!=(const Op& a, const Op& b) { return !(a == b); }
192
193inline Op operator*(const Op& a, const Op& b) { return a.combine(b).wrap(); }
194inline Op& operator*=(Op& a, const Op& b) { a = a * b; return a; }
195
196inline Op Op::inverse() const {
197 int detr = det_rot();
198 if (detr == 0)
199 fail("cannot invert matrix: " + Op{rot, {0,0,0}}.triplet());
200 int d2 = Op::DEN * Op::DEN;
201 Op inv;
202 inv.rot[0][0] = d2 * (rot[1][1] * rot[2][2] - rot[2][1] * rot[1][2]) / detr;
203 inv.rot[0][1] = d2 * (rot[0][2] * rot[2][1] - rot[0][1] * rot[2][2]) / detr;
204 inv.rot[0][2] = d2 * (rot[0][1] * rot[1][2] - rot[0][2] * rot[1][1]) / detr;
205 inv.rot[1][0] = d2 * (rot[1][2] * rot[2][0] - rot[1][0] * rot[2][2]) / detr;
206 inv.rot[1][1] = d2 * (rot[0][0] * rot[2][2] - rot[0][2] * rot[2][0]) / detr;
207 inv.rot[1][2] = d2 * (rot[1][0] * rot[0][2] - rot[0][0] * rot[1][2]) / detr;
208 inv.rot[2][0] = d2 * (rot[1][0] * rot[2][1] - rot[2][0] * rot[1][1]) / detr;
209 inv.rot[2][1] = d2 * (rot[2][0] * rot[0][1] - rot[0][0] * rot[2][1]) / detr;
210 inv.rot[2][2] = d2 * (rot[0][0] * rot[1][1] - rot[1][0] * rot[0][1]) / detr;
211 for (int i = 0; i != 3; ++i)
212 inv.tran[i] = (-tran[0] * inv.rot[i][0]
213 -tran[1] * inv.rot[i][1]
214 -tran[2] * inv.rot[i][2]) / Op::DEN;
215 return inv;
216}
217
218// inverse of Op::float_seitz()
219inline Op seitz_to_op(const std::array<std::array<double,4>, 4>& t) {
220 static_assert(Op::DEN == 24, "");
221 auto check_round = [](double d) {
222 double r = std::round(d * Op::DEN);
223 if (std::fabs(r - d * Op::DEN) > 0.05)
224 fail("all numbers in Seitz matrix must be equal Z/24");
225 return static_cast<int>(r);
226 };
227 Op op;
228 if (std::fabs(t[3][0]) + std::fabs(t[3][1]) + std::fabs(t[3][2]) +
229 std::fabs(t[3][3] - 1) > 1e-3)
230 fail("the last row in Seitz matrix must be [0 0 0 1]");
231 for (int i = 0; i < 3; ++i) {
232 for (int j = 0; j < 3; ++j)
233 op.rot[i][j] = check_round(t[i][j]);
234 op.tran[i] = check_round(t[i][3]);
235 }
236 return op;
237}
238
239
240// TRIPLET -> OP
241
242inline int interpret_miller_character(char c, const std::string& s) {
243 static const signed char values[] =
244 //a b c d e f g h i j k l m n o p q r s t u v w x y z
245 { 1, 2, 3, 0, 0, 0, 0, 1, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 };
246 size_t idx = size_t((c | 0x20) - 'a'); // "|0x20" = to lower
247 if (idx >= sizeof(values) || values[idx] == 0)
248 fail("unexpected character '", c, "' in: ", s);
249 return values[idx] - 1;
250}
251
252inline std::array<int, 4> parse_triplet_part(const std::string& s) {
253 std::array<int, 4> r = { 0, 0, 0, 0 };
254 int num = Op::DEN;
255 const char* c = s.c_str();
256 while (*(c = impl::skip_blank(c))) {
257 if (*c == '+' || *c == '-') {
258 num = (*c == '+' ? Op::DEN : -Op::DEN);
259 c = impl::skip_blank(++c);
260 }
261 if (num == 0)
262 fail("wrong or unsupported triplet format: " + s);
263 int r_idx;
264 int den = 1;
265 if (*c >= '0' && *c <= '9') {
266 // syntax examples in this branch: "1", "-1/2", "+2*x", "1/2 * b"
267 char* endptr;
268 num *= std::strtol(c, &endptr, 10);
269 if (*endptr == '/')
270 den = std::strtol(endptr + 1, &endptr, 10);
271 if (*endptr == '*') {
272 c = impl::skip_blank(endptr + 1);
274 ++c;
275 } else {
276 c = endptr;
277 r_idx = 3;
278 }
279 } else {
280 // syntax examples in this branch: "x", "+a", "-k/3"
282 c = impl::skip_blank(++c);
283 if (*c == '/') {
284 char* endptr;
285 den = std::strtol(c + 1, &endptr, 10);
286 c = endptr;
287 }
288 }
289 if (den != 1) {
290 if (den <= 0 || Op::DEN % den != 0)
291 fail("Wrong denominator " + std::to_string(den) + " in: " + s);
292 num /= den;
293 }
294 r[r_idx] += num;
295 num = 0;
296 }
297 if (num != 0)
298 fail("trailing sign in: " + s);
299 return r;
300}
301
302inline Op parse_triplet(const std::string& s) {
303 if (std::count(s.begin(), s.end(), ',') != 2)
304 fail("expected exactly two commas in triplet");
305 size_t comma1 = s.find(',');
306 size_t comma2 = s.find(',', comma1 + 1);
307 auto a = parse_triplet_part(s.substr(0, comma1));
308 auto b = parse_triplet_part(s.substr(comma1 + 1, comma2 - (comma1 + 1)));
309 auto c = parse_triplet_part(s.substr(comma2 + 1));
310 Op::Rot rot = {a[0], a[1], a[2], b[0], b[1], b[2], c[0], c[1], c[2]};
311 Op::Tran tran = {a[3], b[3], c[3]};
312 return { rot, tran };
313}
314
315
316// OP -> TRIPLET
317
318namespace impl {
319
320// much faster than s += std::to_string(n) for n in 0 ... 99
321inline void append_small_number(std::string& s, int n) {
322 if (n < 0 || n >= 100) {
323 s += std::to_string(n);
324 } else if (n < 10) {
325 s += char('0' + n);
326 } else { // 10 ... 99
327 int tens = n / 10;
328 s += char('0' + tens);
329 s += char('0' + n - 10 * tens);
330 }
331}
332
333inline void append_sign_of(std::string& s, int n) {
334 if (n < 0)
335 s += '-';
336 else if (!s.empty())
337 s += '+';
338}
339
340// append w/DEN fraction reduced to the lowest terms
341inline std::pair<int,int> get_op_fraction(int w) {
342 // Op::DEN == 24 == 2 * 2 * 2 * 3
343 int denom = 1;
344 for (int i = 0; i != 3; ++i)
345 if (w % 2 == 0) // 2, 2, 2
346 w /= 2;
347 else
348 denom *= 2;
349 if (w % 3 == 0) // 3
350 w /= 3;
351 else
352 denom *= 3;
353 return {w, denom};
354}
355
356inline void append_fraction(std::string& s, std::pair<int,int> frac) {
357 append_small_number(s, frac.first);
358 if (frac.second != 1) {
359 s += '/';
360 append_small_number(s, frac.second);
361 }
362}
363
364} // namespace impl
365
366inline std::string make_triplet_part(const std::array<int, 3>& xyz, int w,
367 char style='x') {
368 std::string s;
369 const char* letters = "xyz hkl abc XYZ HKL ABC";
370 switch(style | 0x20) { // |0x20 converts to lower case
371 case 'x': break;
372 case 'h': letters += 4; break;
373 case 'a': letters += 8; break;
374 default: fail("unexpected triplet style: ", style);
375 }
376 if (!(style & 0x20)) // not lower
377 letters += 12;
378 for (int i = 0; i != 3; ++i)
379 if (xyz[i] != 0) {
380 impl::append_sign_of(s, xyz[i]);
381 int a = std::abs(xyz[i]);
382 if (a != Op::DEN) {
383 std::pair<int,int> frac = impl::get_op_fraction(a);
384 if (frac.first == 1) { // e.g. "x/3"
385 s += letters[i];
386 s += '/';
387 impl::append_small_number(s, frac.second);
388 } else { // e.g. "2/3*x"
389 impl::append_fraction(s, frac);
390 s += '*';
391 s += letters[i];
392 }
393 } else {
394 s += letters[i];
395 }
396 }
397 if (w != 0) {
398 impl::append_sign_of(s, w);
399 std::pair<int,int> frac = impl::get_op_fraction(std::abs(w));
400 impl::append_fraction(s, frac);
401 }
402 return s;
403}
404
405inline std::string Op::triplet(char style) const {
406 return make_triplet_part(rot[0], tran[0], style) +
407 "," + make_triplet_part(rot[1], tran[1], style) +
408 "," + make_triplet_part(rot[2], tran[2], style);
409}
410
411
412// GROUPS OF OPERATIONS
413
414// corresponds to Table A1.4.2.2 in ITfC vol.B (edition 2010)
415inline std::vector<Op::Tran> centring_vectors(char centring_type) {
416 constexpr int h = Op::DEN / 2;
417 constexpr int t = Op::DEN / 3;
418 constexpr int d = 2 * t;
419 // note: find_centering() depends on the order of operations in vector
420 switch (centring_type & ~0x20) {
421 case 'P': return {{0, 0, 0}};
422 case 'A': return {{0, 0, 0}, {0, h, h}};
423 case 'B': return {{0, 0, 0}, {h, 0, h}};
424 case 'C': return {{0, 0, 0}, {h, h, 0}};
425 case 'I': return {{0, 0, 0}, {h, h, h}};
426 case 'R': return {{0, 0, 0}, {d, t, t}, {t, d, d}};
427 // hall_symbols.html has no H, ITfC 2010 has no S and T
428 case 'H': return {{0, 0, 0}, {d, t, 0}, {t, d, 0}};
429 case 'S': return {{0, 0, 0}, {t, t, d}, {d, t, d}};
430 case 'T': return {{0, 0, 0}, {t, d, t}, {d, t, d}};
431 case 'F': return {{0, 0, 0}, {0, h, h}, {h, 0, h}, {h, h, 0}};
432 default: fail("not a centring type: ", centring_type);
433 }
434}
435
436
437struct GroupOps {
438 std::vector<Op> sym_ops;
439 std::vector<Op::Tran> cen_ops;
440
441 int order() const { return static_cast<int>(sym_ops.size()*cen_ops.size()); }
442
444 void add_missing_elements_part2(const std::vector<Op>& gen,
445 size_t max_size, bool ignore_bad_gen);
446
448 size_t init_size = sym_ops.size();
449 sym_ops.reserve(2 * init_size);
450 for (const Op& op : sym_ops) {
451 Op::Rot neg = op.negated_rot();
452 if (find_by_rotation(neg)) {
453 sym_ops.resize(init_size);
454 return false;
455 }
456 sym_ops.push_back({neg, op.tran});
457 }
458 return true;
459 }
460
461 char find_centering() const {
462 if (cen_ops.size() == 1 && cen_ops[0] == Op::Tran{0, 0, 0})
463 return 'P';
464 std::vector<Op::Tran> trans = cen_ops;
465 std::sort(trans.begin(), trans.end());
466 for (char c : {'A', 'B', 'C', 'I', 'F', 'R', 'H', 'S', 'T'}) {
467 std::vector<Op::Tran> c_vectors = centring_vectors(c);
468 if (c == 'R' || c == 'H') // these two are returned not sorted
469 std::swap(c_vectors[1], c_vectors[2]);
470 if (trans == c_vectors)
471 return c;
472 }
473 return 0;
474 }
475
477 for (Op& op : sym_ops)
478 if (op.rot == r)
479 return &op;
480 return nullptr;
481 }
482
483 const Op* find_by_rotation(const Op::Rot& r) const {
484 return const_cast<GroupOps*>(this)->find_by_rotation(r);
485 }
486
487 bool is_centrosymmetric() const {
488 return find_by_rotation(Op::inversion_rot()) != nullptr;
489 }
490
491 bool is_reflection_centric(const Op::Miller& hkl) const {
492 Op::Miller mhkl = {{-Op::DEN * hkl[0], -Op::DEN * hkl[1], -Op::DEN * hkl[2]}};
493 for (const Op& op : sym_ops)
494 if (op.apply_to_hkl_without_division(hkl) == mhkl)
495 return true;
496 return false;
497 }
498
500 Op::Miller denh = {{Op::DEN * hkl[0], Op::DEN * hkl[1], Op::DEN * hkl[2]}};
501 int epsilon = 0;
502 for (const Op& op : sym_ops)
503 if (op.apply_to_hkl_without_division(hkl) == denh)
504 ++epsilon;
505 return epsilon;
506 }
507 int epsilon_factor(const Op::Miller& hkl) const {
508 return epsilon_factor_without_centering(hkl) * (int) cen_ops.size();
509 }
510
511 static bool has_phase_shift(const Op::Tran& c, const Op::Miller& hkl) {
512 return (hkl[0] * c[0] + hkl[1] * c[1] + hkl[2] * c[2]) % Op::DEN != 0;
513 }
514
515 bool is_systematically_absent(const Op::Miller& hkl) const {
516 for (auto i = cen_ops.begin() + 1; i != cen_ops.end(); ++i)
517 if (has_phase_shift(*i, hkl))
518 return true;
519 Op::Miller denh = {{Op::DEN * hkl[0], Op::DEN * hkl[1], Op::DEN * hkl[2]}};
520 for (auto op = sym_ops.begin() + 1; op != sym_ops.end(); ++op)
521 if (op->apply_to_hkl_without_division(hkl) == denh) {
522 for (const Op::Tran& c : cen_ops)
523 if (has_phase_shift({{op->tran[0] + c[0],
524 op->tran[1] + c[1],
525 op->tran[2] + c[2]}}, hkl))
526 return true;
527 }
528 return false;
529 }
530
531 void change_basis_impl(const Op& cob, const Op& inv) {
532 if (sym_ops.empty() || cen_ops.empty())
533 return;
534
535 // Apply change-of-basis to sym_ops.
536 // Ignore the first item in sym_ops -- it's identity.
537 for (auto op = sym_ops.begin() + 1; op != sym_ops.end(); ++op)
538 *op = cob.combine(*op).combine(inv).wrap();
539
540 // The number of centering vectors may be different.
541 // As an ad-hoc method (not proved to be robust) add lattice points
542 // from a super-cell.
543 int idet = inv.det_rot() / (Op::DEN * Op::DEN * Op::DEN);
544 if (idet > 1) {
545 std::vector<Op::Tran> new_cen_ops;
546 new_cen_ops.reserve(cen_ops.size() * idet * idet * idet);
547 for (int i = 0; i < idet; ++i)
548 for (int j = 0; j < idet; ++j)
549 for (int k = 0; k < idet; ++k)
550 for (Op::Tran& cen : cen_ops)
551 new_cen_ops.push_back({i * Op::DEN + cen[0],
552 j * Op::DEN + cen[1],
553 k * Op::DEN + cen[2]});
554 cen_ops.swap(new_cen_ops);
555 }
556
557 // Apply change-of-basis to centering vectors
558 Op cvec = Op::identity();
559 for (auto tr = cen_ops.begin() + 1; tr != cen_ops.end(); ++tr) {
560 cvec.tran = *tr;
561 *tr = cob.combine(cvec).combine(inv).wrap().tran;
562 }
563
564 // Remove redundant centering vectors.
565 for (int i = static_cast<int>(cen_ops.size()) - 1; i > 0; --i)
566 for (int j = i - 1; j >= 0; --j)
567 if (cen_ops[i] == cen_ops[j]) {
568 cen_ops.erase(cen_ops.begin() + i);
569 break;
570 }
571 }
572
573 void change_basis_forward(const Op& cob) { change_basis_impl(cob, cob.inverse()); }
574 void change_basis_backward(const Op& inv) { change_basis_impl(inv.inverse(), inv); }
575
576 std::vector<Op> all_ops_sorted() const {
577 std::vector<Op> ops;
578 ops.reserve(sym_ops.size() * cen_ops.size());
579 for (const Op& so : sym_ops)
580 for (const Op::Tran& co : cen_ops)
581 ops.push_back(so.add_centering(co));
582 std::sort(ops.begin(), ops.end());
583 return ops;
584 }
585
586 Op get_op(int n) const {
587 int n_cen = n / (int) sym_ops.size();
588 int n_sym = n % (int) sym_ops.size();
589 return sym_ops.at(n_sym).add_centering(cen_ops.at(n_cen));
590 }
591
592 bool is_same_as(const GroupOps& other) const {
593 if (cen_ops.size() != other.cen_ops.size() ||
594 sym_ops.size() != other.sym_ops.size())
595 return false;
596 return all_ops_sorted() == other.all_ops_sorted();
597 }
598
599 bool has_same_centring(const GroupOps& other) const {
600 if (cen_ops.size() != other.cen_ops.size())
601 return false;
602 if (std::is_sorted(cen_ops.begin(), cen_ops.end()) &&
603 std::is_sorted(other.cen_ops.begin(), other.cen_ops.end()))
604 return cen_ops == other.cen_ops;
605 std::vector<Op::Tran> v1 = cen_ops;
606 std::vector<Op::Tran> v2 = other.cen_ops;
607 std::sort(v1.begin(), v1.end());
608 std::sort(v2.begin(), v2.end());
609 return v1 == v2;
610 }
611
612 bool has_same_rotations(const GroupOps& other) const {
613 if (sym_ops.size() != other.sym_ops.size())
614 return false;
615 auto sorted_rotations = [](const GroupOps& g) {
616 std::vector<Op::Rot> r(g.sym_ops.size());
617 for (size_t i = 0; i != r.size(); ++i)
618 r[i] = g.sym_ops[i].rot;
619 std::sort(r.begin(), r.end());
620 return r;
621 };
622 return sorted_rotations(*this) == sorted_rotations(other);
623 }
624
625 // minimal multiplicity for real-space grid in each direction
626 // examples: 1,2,1 for P21, 1,1,6 for P61
627 std::array<int, 3> find_grid_factors() const {
628 const int T = Op::DEN;
629 int r[3] = {T, T, T};
630 for (Op op : *this)
631 for (int i = 0; i != 3; ++i)
632 if (op.tran[i] != 0 && op.tran[i] < r[i])
633 r[i] = op.tran[i];
634 return {T / r[0], T / r[1], T / r[2]};
635 }
636
637 bool are_directions_symmetry_related(int u, int v) const {
638 for (const Op& op : sym_ops)
639 if (op.rot[u][v] != 0)
640 return true;
641 return false;
642 }
643
644 // remove translation part of sym_ops
646 GroupOps r(*this);
647 for (Op& op : r.sym_ops)
648 op.tran[0] = op.tran[1] = op.tran[2] = 0;
649 return r;
650 }
651
652 struct Iter {
654 int n_sym, n_cen;
655 void operator++() {
656 if (++n_sym == (int) gops.sym_ops.size()) {
657 ++n_cen;
658 n_sym = 0;
659 }
660 }
661 Op operator*() const {
662 return gops.sym_ops.at(n_sym).translated(gops.cen_ops.at(n_cen)).wrap();
663 }
664 bool operator==(const Iter& other) const {
665 return n_sym == other.n_sym && n_cen == other.n_cen;
666 }
667 bool operator!=(const Iter& other) const { return !(*this == other); }
668 };
669
670 Iter begin() const { return {*this, 0, 0}; };
671 Iter end() const { return {*this, 0, (int) cen_ops.size()}; };
672};
673
674inline void GroupOps::add_missing_elements() {
675 // We always keep identity as sym_ops[0].
676 if (sym_ops.empty() || sym_ops[0] != Op::identity())
677 fail("oops");
678 if (sym_ops.size() == 1)
679 return;
680 constexpr size_t max_size = 1024;
681 // Below we assume that all centring vectors are already known (in cen_ops)
682 // so when checking for a new element we compare only the 3x3 matrix.
683 // Dimino's algorithm. https://physics.stackexchange.com/a/351400/95713
684 std::vector<Op> gen(sym_ops.begin() + 1, sym_ops.end());
685 sym_ops.resize(2);
686 const Op::Rot idrot = Op::identity().rot;
687 for (Op g = sym_ops[1] * sym_ops[1]; g.rot != idrot; g *= sym_ops[1]) {
688 sym_ops.push_back(g);
689 if (sym_ops.size() > max_size)
690 fail("Too many elements in the group - bad generators");
691 }
692 // the rest is in separate function b/c it's reused in twin.hpp
693 add_missing_elements_part2(gen, max_size, false);
694}
695
696inline void GroupOps::add_missing_elements_part2(const std::vector<Op>& gen,
697 size_t max_size, bool ignore_bad_gen) {
698 for (size_t i = 1; i < gen.size(); ++i) {
699 std::vector<Op> coset_repr(1, Op::identity());
700 size_t init_size = sym_ops.size();
701 for (;;) {
702 size_t len = coset_repr.size();
703 for (size_t j = 0; j != len; ++j) {
704 for (size_t n = 0; n != i + 1; ++n) {
705 Op sg = gen[n] * coset_repr[j];
706 if (find_by_rotation(sg.rot) == nullptr) {
707 sym_ops.push_back(sg);
708 for (size_t k = 1; k != init_size; ++k)
709 sym_ops.push_back(sg * sym_ops[k]);
710 coset_repr.push_back(sg);
711 }
712 }
713 }
714 if (len == coset_repr.size())
715 break;
716 if (sym_ops.size() > max_size) {
717 if (!ignore_bad_gen)
718 fail("Too many elements in the group - bad generators");
719 // ignore this generator and continue with the next one
720 sym_ops.resize(init_size);
721 break;
722 }
723 }
724 }
725}
726
727// Create GroupOps from Ops by separating centering vectors
728inline GroupOps split_centering_vectors(const std::vector<Op>& ops) {
729 const Op identity = Op::identity();
730 GroupOps go;
731 go.sym_ops.push_back(identity);
732 for (const Op& op : ops)
733 if (Op* old_op = go.find_by_rotation(op.rot)) {
734 if (op.rot == identity.rot) // pure shift
735 go.cen_ops.push_back(op.tran);
736 if (op.tran == identity.tran) // or rather |op.tran| < |old_op->tran| ?
737 old_op->tran = op.tran;
738 } else {
739 go.sym_ops.push_back(op);
740 }
741 return go;
742}
743
744// INTERPRETING HALL SYMBOLS
745// based on both ITfC vol.B ch.1.4 (2010)
746// and http://cci.lbl.gov/sginfo/hall_symbols.html
747
748// matrices for Nz from Table 3 and 4 from hall_symbols.html
750 constexpr int d = Op::DEN;
751 switch (N) {
752 case 1: return {d,0,0, 0,d,0, 0,0,d};
753 case 2: return {-d,0,0, 0,-d,0, 0,0,d};
754 case 3: return {0,-d,0, d,-d,0, 0,0,d};
755 case 4: return {0,-d,0, d,0,0, 0,0,d};
756 case 6: return {d,-d,0, d,0,0, 0,0,d};
757 case '\'': return {0,-d,0, -d,0,0, 0,0,-d};
758 case '"': return {0,d,0, d,0,0, 0,0,-d};
759 case '*': return {0,0,d, d,0,0, 0,d,0};
760 default: fail("incorrect axis definition");
761 }
762}
764 constexpr int h = Op::DEN / 2;
765 constexpr int q = Op::DEN / 4;
766 switch (symbol) {
767 case 'a': return {h, 0, 0};
768 case 'b': return {0, h, 0};
769 case 'c': return {0, 0, h};
770 case 'n': return {h, h, h};
771 case 'u': return {q, 0, 0};
772 case 'v': return {0, q, 0};
773 case 'w': return {0, 0, q};
774 case 'd': return {q, q, q};
775 default: fail(std::string("unknown symbol: ") + symbol);
776 }
777}
778
779inline Op hall_matrix_symbol(const char* start, const char* end,
780 int pos, int& prev) {
781 Op op = Op::identity();
782 bool neg = (*start == '-');
783 const char* p = (neg ? start + 1 : start);
784 if (*p < '1' || *p == '5' || *p > '6')
785 fail("wrong n-fold order notation: " + std::string(start, end));
786 int N = *p++ - '0';
787 int fractional_tran = 0;
788 char principal_axis = '\0';
789 char diagonal_axis = '\0';
790 for (; p < end; ++p) {
791 if (*p >= '1' && *p <= '5') {
792 if (fractional_tran != '\0')
793 fail("two numeric subscripts");
794 fractional_tran = *p - '0';
795 } else if (*p == '\'' || *p == '"' || *p == '*') {
796 if (N != (*p == '*' ? 3 : 2))
797 fail("wrong symbol: " + std::string(start, end));
798 diagonal_axis = *p;
799 } else if (*p == 'x' || *p == 'y' || *p == 'z') {
800 principal_axis = *p;
801 } else {
803 }
804 }
805 // fill in implicit values
806 if (!principal_axis && !diagonal_axis) {
807 if (pos == 1) {
808 principal_axis = 'z';
809 } else if (pos == 2 && N == 2) {
810 if (prev == 2 || prev == 4)
811 principal_axis = 'x';
812 else if (prev == 3 || prev == 6)
813 diagonal_axis = '\'';
814 } else if (pos == 3 && N == 3) {
815 diagonal_axis = '*';
816 } else if (N != 1) {
817 fail("missing axis");
818 }
819 }
820 // get the operation
822 if (neg)
823 op.rot = op.negated_rot();
824 auto alter_order = [](const Op::Rot& r, int i, int j, int k) {
825 return Op::Rot{ r[i][i], r[i][j], r[i][k],
826 r[j][i], r[j][j], r[j][k],
827 r[k][i], r[k][j], r[k][k] };
828 };
829 if (principal_axis == 'x')
830 op.rot = alter_order(op.rot, 2, 0, 1);
831 else if (principal_axis == 'y')
832 op.rot = alter_order(op.rot, 1, 2, 0);
833 if (fractional_tran)
834 op.tran[principal_axis - 'x'] += Op::DEN / N * fractional_tran;
835 prev = N;
836 return op;
837}
838
839// Parses either short (0 0 1) or long notation (x,y,z+1/12)
840// but without multipliers (such as 1/2x) to keep things simple for now.
841inline Op parse_hall_change_of_basis(const char* start, const char* end) {
842 if (std::memchr(start, ',', end - start) != nullptr) // long symbol
843 return parse_triplet(std::string(start, end));
844 // short symbol (0 0 1)
845 Op cob = Op::identity();
846 char* endptr;
847 for (int i = 0; i != 3; ++i) {
848 cob.tran[i] = std::strtol(start, &endptr, 10) % 12 * (Op::DEN / 12);
849 start = endptr;
850 }
851 if (endptr != end)
852 fail("unexpected change-of-basis format: " + std::string(start, end));
853 return cob;
854}
855
856inline GroupOps generators_from_hall(const char* hall) {
857 auto find_blank = [](const char* p) {
858 while (*p != '\0' && *p != ' ' && *p != '\t' && *p != '_') // '_' == ' '
859 ++p;
860 return p;
861 };
862 if (hall == nullptr)
863 fail("null");
864 hall = impl::skip_blank(hall);
866 ops.sym_ops.emplace_back(Op::identity());
867 bool centrosym = (hall[0] == '-');
868 const char* lat = impl::skip_blank(centrosym ? hall + 1 : hall);
869 if (!lat)
870 fail("not a hall symbol: " + std::string(hall));
871 ops.cen_ops = centring_vectors(*lat);
872 int counter = 0;
873 int prev = 0;
874 const char* part = impl::skip_blank(lat + 1);
875 while (*part != '\0' && *part != '(') {
876 const char* space = find_blank(part);
877 ++counter;
878 if (part[0] != '1' || (part[1] != ' ' && part[1] != '\0')) {
879 Op op = hall_matrix_symbol(part, space, counter, prev);
880 ops.sym_ops.emplace_back(op);
881 }
882 part = impl::skip_blank(space);
883 }
884 if (centrosym)
885 ops.sym_ops.push_back({Op::identity().negated_rot(), {0,0,0}});
886 if (*part == '(') {
887 const char* rb = std::strchr(part, ')');
888 if (!rb)
889 fail("missing ')': " + std::string(hall));
890 if (ops.sym_ops.empty())
891 fail("misplaced translation: " + std::string(hall));
892 ops.change_basis_forward(parse_hall_change_of_basis(part + 1, rb));
893
894 if (*impl::skip_blank(find_blank(rb + 1)) != '\0')
895 fail("unexpected characters after ')': " + std::string(hall));
896 }
897 return ops;
898}
899
900inline GroupOps symops_from_hall(const char* hall) {
902 ops.add_missing_elements();
903 return ops;
904}
905
906// CRYSTAL SYSTEMS, POINT GROUPS AND LAUE CLASSES
907
908enum class CrystalSystem : unsigned char {
910};
911
913 static const char* names[7] = {
914 "triclinic", "monoclinic", "orthorhombic", "tetragonal",
915 "trigonal", "hexagonal", "cubic"
916 };
917 return names[static_cast<int>(system)];
918}
919
920enum class PointGroup : unsigned char {
921 C1=0, Ci, C2, Cs, C2h, D2, C2v, D2h, C4, S4, C4h, D4, C4v, D2d, D4h, C3,
922 C3i, D3, C3v, D3d, C6, C3h, C6h, D6, C6v, D3h, D6h, T, Th, O, Td, Oh
923};
924
925inline const char* point_group_hm(PointGroup pg) {
926 static const char hm_pointgroup_names[32][6] = {
927 "1", "-1", "2", "m", "2/m", "222", "mm2", "mmm",
928 "4", "-4", "4/m", "422", "4mm", "-42m", "4/mmm", "3",
929 "-3", "32", "3m", "-3m", "6", "-6", "6/m", "622",
930 "6mm", "-62m", "6/mmm", "23", "m-3", "432", "-43m", "m-3m",
931 };
932 return hm_pointgroup_names[static_cast<int>(pg)];
933}
934
935// http://reference.iucr.org/dictionary/Laue_class
936enum class Laue : unsigned char {
937 L1=0, L2m, Lmmm, L4m, L4mmm, L3, L3m, L6m, L6mmm, Lm3, Lm3m
938};
939
941 static const Laue laue[32] = {
942 Laue::L1, Laue::L1,
943 Laue::L2m, Laue::L2m, Laue::L2m,
944 Laue::Lmmm, Laue::Lmmm, Laue::Lmmm,
945 Laue::L4m, Laue::L4m, Laue::L4m,
946 Laue::L4mmm, Laue::L4mmm, Laue::L4mmm, Laue::L4mmm,
947 Laue::L3, Laue::L3,
948 Laue::L3m, Laue::L3m, Laue::L3m,
949 Laue::L6m, Laue::L6m, Laue::L6m,
950 Laue::L6mmm, Laue::L6mmm, Laue::L6mmm, Laue::L6mmm,
951 Laue::Lm3, Laue::Lm3,
952 Laue::Lm3m, Laue::Lm3m, Laue::Lm3m,
953 };
954 return laue[static_cast<int>(pg)];
955}
956
957// return centrosymmetric pointgroup from the Laue class
959 static const PointGroup pg[11] = {
960 PointGroup::Ci, PointGroup::C2h, PointGroup::D2h, PointGroup::C4h,
961 PointGroup::D4h, PointGroup::C3i, PointGroup::D3d, PointGroup::C6h,
962 PointGroup::D6h, PointGroup::Th, PointGroup::Oh
963 };
964 return pg[static_cast<int>(laue)];
965}
966
967inline const char* laue_class_str(Laue laue) {
969}
970
972 static const CrystalSystem crystal_systems[11] = {
973 CrystalSystem::Triclinic,
974 CrystalSystem::Monoclinic,
975 CrystalSystem::Orthorhombic,
976 CrystalSystem::Tetragonal, CrystalSystem::Tetragonal,
977 CrystalSystem::Trigonal, CrystalSystem::Trigonal,
978 CrystalSystem::Hexagonal, CrystalSystem::Hexagonal,
979 CrystalSystem::Cubic, CrystalSystem::Cubic
980 };
981 return crystal_systems[static_cast<int>(laue)];
982}
983
987
989 // 0x20=Sohncke, 0x40=enantiomorphic, 0x80=symmorphic
990 enum : unsigned char { S=0x20, E=(0x20|0x40), Y=0x80, Z=(0x20|0x80) };
991 static const unsigned char indices[230] = {
992 0|Z, 1|Y, 2|Z, 2|S, 2|Z, 3|Y, 3, 3|Y, 3, 4|Y, // 1-10
993 4, 4|Y, 4, 4, 4, 5|Z, 5|S, 5|S, 5|S, 5|S, // 11-20
994 5|Z, 5|Z, 5|Z, 5|S, 6|Y, 6, 6, 6, 6, 6, // 21-30
995 6, 6, 6, 6, 6|Y, 6, 6, 6|Y, 6, 6, // 31-40
996 6, 6|Y, 6, 6|Y, 6, 6, 7|Y, 7, 7, 7, // 41-50
997 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 51-60
998 7, 7, 7, 7, 7|Y, 7, 7, 7, 7|Y, 7, // 61-70
999 7|Y, 7, 7, 7, 8|Z, 8|E, 8|S, 8|E, 8|Z, 8|S, // 71-80
1000 9|Y, 9|Y, 10|Y, 10, 10, 10, 10|Y, 10, 11|Z, 11|S, // 81-90
1001 11|E, 11|E, 11|S, 11|S, 11|E, 11|E, 11|Z, 11|S, 12|Y, 12, // 91-100
1002 12, 12, 12, 12, 12, 12, 12|Y, 12, 12, 12, // 101-110
1003 13|Y, 13, 13, 13, 13|Y, 13, 13, 13, 13|Y, 13, // 111-120
1004 13|Y, 13, 14|Y, 14, 14, 14, 14, 14, 14, 14, // 121-130
1005 14, 14, 14, 14, 14, 14, 14, 14, 14|Y, 14, // 131-140
1006 14, 14, 15|Z, 15|E, 15|E, 15|Z, 16|Y, 16|Y, 17|Z, 17|Z, // 141-150
1007 17|E, 17|E, 17|E, 17|E, 17|Z, 18|Y, 18|Y, 18, 18, 18|Y, // 151-160
1008 18, 19|Y, 19, 19|Y, 19, 19|Y, 19, 20|Z, 20|E, 20|E, // 161-170
1009 20|E, 20|E, 20|S, 21|Y, 22|Y, 22, 23|Z, 23|E, 23|E, 23|E, // 171-180
1010 23|E, 23|S, 24|Y, 24, 24, 24, 25|Y, 25, 25|Y, 25, // 181-190
1011 26|Y, 26, 26, 26, 27|Z, 27|Z, 27|Z, 27|S, 27|S, 28|Y, // 191-200
1012 28, 28|Y, 28, 28|Y, 28, 28, 29|Z, 29|S, 29|Z, 29|S, // 201-210
1013 29|Z, 29|E, 29|E, 29|S, 30|Y, 30|Y, 30|Y, 30, 30, 30, // 211-220
1014 31|Y, 31, 31, 31, 31|Y, 31, 31, 31, 31|Y, 31 // 221-230
1015 };
1016 return indices[space_group_number-1];
1017}
1018
1021 return static_cast<PointGroup>(n & 0x1f);
1022}
1023
1024// true for 65 Sohncke (non-enantiogenic) space groups
1027}
1028
1029// true for 22 space groups (11 enantiomorphic pairs)
1033
1034// true for 73 space groups
1037}
1038
1044 constexpr int D = Op::DEN;
1045 switch (space_group_number) {
1046 case 43: return {D/8, D/8, 0};
1047 case 80: return {D/4, 0, 0};
1048 case 98: return {D/4, 0, D/8};
1049 case 109: return {D/4, 0, 0};
1050 case 110: return {D/4, 0, 0};
1051 case 122: return {D/4, 0, D/8};
1052 case 210: return {D/8, D/8, D/8};
1053 default: return {0, 0, 0};
1054 }
1055}
1056
1057// Generated by tools/gen_sg_table.py.
1058inline const char* get_basisop(int basisop_idx) {
1059 static const char* basisops[49] = {
1060 "x,y,z", // 0
1061 "z,x,y", // 1
1062 "y,z,x", // 2
1063 "z,y,-x", // 3
1064 "x,y,-x+z", // 4
1065 "-x,z,y", // 5
1066 "-x+z,x,y", // 6
1067 "y,-x,z", // 7
1068 "y,-x+z,x", // 8
1069 "x-z,y,z", // 9
1070 "z,x-z,y", // 10
1071 "y,z,x-z", // 11
1072 "z,y,-x+z", // 12
1073 "x+z,y,-x", // 13
1074 "x+1/4,y+1/4,z", // 14
1075 "-x+z,z,y", // 15
1076 "-x,x+z,y", // 16
1077 "y,-x+z,z", // 17
1078 "y,-x,x+z", // 18
1079 "x+1/4,y-1/4,z", // 19
1080 "x-1/4,y-1/4,z-1/4", // 20
1081 "x-1/4,y-1/4,z", // 21
1082 "z,x-1/4,y-1/4", // 22
1083 "y-1/4,z,x-1/4", // 23
1084 "x-1/2,y-1/4,z+1/4", // 24
1085 "z+1/4,x-1/2,y-1/4", // 25
1086 "y-1/4,z+1/4,x-1/2", // 26
1087 "x+1/8,y+1/8,z+1/8", // 27
1088 "x+1/4,y-1/4,z+1/4", // 28
1089 "x-1/4,y+1/4,z", // 29
1090 "x+1/4,y+1/4,z+1/4", // 30
1091 "x,y+1/4,z+1/8", // 31
1092 "x-1/4,y+1/4,z+1/4", // 32
1093 "x-1/4,y+1/4,z-1/4", // 33
1094 "x-1/2,y+1/4,z+1/8", // 34
1095 "x-1/2,y+1/4,z-3/8", // 35
1096 "-y+z,x+z,-x+y+z", // 36
1097 "x-1/8,y-1/8,z-1/8", // 37
1098 "x+1/4,y+1/4,-x+z-1/4", // 38
1099 "x+1/4,y,z", // 39
1100 "x,y,z+1/4", // 40
1101 "-x,-y/2+z/2,y/2+z/2", // 41
1102 "-x/2+z/2,-y,x/2+z/2", // 42
1103 "x/2+y/2,x/2-y/2,-z", // 43
1104 "y/2+z/2,x/2+z/2,x/2+y/2", // 44
1105 "-x/2+y/2+z/2,x/2-y/2+z/2,x/2+y/2-z/2", // 45
1106 "-x/2+z,x/2,y", // 46
1107 "x-z/2,y,z/2", // 47
1108 "x/2+y/2,-x/2+y/2,z", // 48
1109 };
1110 return basisops[basisop_idx];
1111}
1112
1113// Returns a change-of-basis operator for centred -> primitive transformation.
1114// The same operator as inverse of z2p_op in sgtbx.
1115inline Op::Rot centred_to_primitive(char centring_type) {
1116 constexpr int D = Op::DEN;
1117 constexpr int H = Op::DEN / 2;
1118 constexpr int T = Op::DEN / 3;
1119 switch (centring_type) {
1120 case 'P': return {D,0,0, 0,D,0, 0,0,D};
1121 case 'A': return {-D,0,0, 0,-H,H, 0,H,H};
1122 case 'B': return {-H,0,H, 0,-D,0, H,0,H};
1123 case 'C': return {H,H,0, H,-H,0, 0,0,-D};
1124 case 'I': return {-H,H,H, H,-H,H, H,H,-H};
1125 case 'R': return {2*T,-T,-T, T,T,-2*T, T,T,T};
1126 case 'H': return {2*T,-T,0, T,T,0, 0,0,D}; // not used normally
1127 case 'F': return {0,H,H, H,0,H, H,H,0};
1128 default: fail("not a centring type: ", centring_type);
1129 }
1130}
1131
1132
1133// LIST OF CRYSTALLOGRAPHIC SPACE GROUPS
1134
1135struct SpaceGroup { // typically 44 bytes
1137 int ccp4;
1138 char hm[11]; // Hermann-Mauguin (international) notation
1139 char ext;
1140 char qualifier[5];
1141 char hall[15];
1143
1144 std::string xhm() const {
1145 std::string ret = hm;
1146 if (ext) {
1147 ret += ':';
1148 ret += ext;
1149 }
1150 return ret;
1151 }
1152
1153 char centring_type() const { return ext == 'R' ? 'P' : hm[0]; }
1154
1155 // (old) CCP4 spacegroup names start with H for hexagonal setting
1156 char ccp4_lattice_type() const { return ext == 'H' ? 'H' : hm[0]; }
1157
1158 // P 1 2 1 -> P2, but P 1 1 2 -> P112. R 3:H -> H3.
1159 std::string short_name() const {
1160 std::string s(hm);
1161 size_t len = s.size();
1162 if (len > 6 && s[2] == '1' && s[len - 2] == ' ' && s[len - 1] == '1')
1163 s = s[0] + s.substr(4, len - 4 - 2);
1164 if (ext == 'H')
1165 s[0] = 'H';
1166 s.erase(std::remove(s.begin(), s.end(), ' '), s.end());
1167 return s;
1168 }
1169
1170 // As explained in Phenix newsletter CCN_2011_01.pdf#page=12
1171 // the PDB uses own, non-standard symbols for rhombohedral space groups.
1172 std::string pdb_name() const {
1173 std::string s;
1174 s += ccp4_lattice_type();
1175 s += hm+1;
1176 return s;
1177 }
1178
1179 bool is_sohncke() const { return gemmi::is_sohncke(number); }
1180 bool is_enantiomorphic() const { return gemmi::is_enantiomorphic(number); }
1181 bool is_symmorphic() const { return gemmi::is_symmorphic(number); }
1182 PointGroup point_group() const { return gemmi::point_group(number); }
1183 const char* point_group_hm() const {
1185 }
1187 const char* laue_str() const { return laue_class_str(laue_class()); }
1191 const char* crystal_system_str() const {
1193 }
1194 bool is_centrosymmetric() const {
1195 return laue_to_pointgroup(laue_class()) == point_group();
1196 }
1197
1200 if (crystal_system() == CrystalSystem::Monoclinic)
1201 return qualifier[qualifier[0] == '-' ? 1 : 0];
1202 return '\0';
1203 }
1204
1205 const char* basisop_str() const { return get_basisop(basisop_idx); }
1206 Op basisop() const { return parse_triplet(basisop_str()); }
1207 bool is_reference_setting() const { return basisop_idx == 0; }
1208
1210 return {gemmi::centred_to_primitive(centring_type()), {0,0,0}};
1211 }
1212
1215 if (is_centrosymmetric())
1216 return Op::identity();
1218 Op op{Op::inversion_rot(), {2*t[0], 2*t[1], 2*t[2]}};
1219 if (!is_reference_setting()) {
1220 Op b = basisop();
1221 op = b.combine(op).combine(b.inverse());
1222 }
1223 return op;
1224 }
1225
1226 GroupOps operations() const { return symops_from_hall(hall); }
1227};
1228
1230 char hm[11];
1231 char ext;
1232 int pos;
1233};
1234
1235// the template here is only to substitute C++17 inline variables
1236// https://stackoverflow.com/questions/38043442/how-do-inline-variables-work
1237namespace impl {
1238
1239template<class Dummy>
1240struct Tables_
1241{
1242 static const SpaceGroup main[559];
1243 static const SpaceGroupAltName alt_names[28];
1244 static const unsigned char ccp4_hkl_asu[230];
1245};
1246
1247template<class Dummy>
1248const SpaceGroup Tables_<Dummy>::main[559] = {
1249 // This table was generated by tools/gen_sg_table.py.
1250 // First 530 entries in the same order as in SgInfo, sgtbx and ITB.
1251 // Note: spacegroup 68 has three duplicates with different H-M names.
1252 { 1, 1, "P 1" , 0, "", "P 1" , 0 }, // 0
1253 { 2, 2, "P -1" , 0, "", "-P 1" , 0 }, // 1
1254 { 3, 3, "P 1 2 1" , 0, "b", "P 2y" , 0 }, // 2
1255 { 3, 1003, "P 1 1 2" , 0, "c", "P 2" , 1 }, // 3
1256 { 3, 0, "P 2 1 1" , 0, "a", "P 2x" , 2 }, // 4
1257 { 4, 4, "P 1 21 1" , 0, "b", "P 2yb" , 0 }, // 5
1258 { 4, 1004, "P 1 1 21" , 0, "c", "P 2c" , 1 }, // 6
1259 { 4, 0, "P 21 1 1" , 0, "a", "P 2xa" , 2 }, // 7
1260 { 5, 5, "C 1 2 1" , 0, "b1", "C 2y" , 0 }, // 8
1261 { 5, 2005, "A 1 2 1" , 0, "b2", "A 2y" , 3 }, // 9
1262 { 5, 4005, "I 1 2 1" , 0, "b3", "I 2y" , 4 }, // 10
1263 { 5, 0, "A 1 1 2" , 0, "c1", "A 2" , 1 }, // 11
1264 { 5, 1005, "B 1 1 2" , 0, "c2", "B 2" , 5 }, // 12
1265 { 5, 0, "I 1 1 2" , 0, "c3", "I 2" , 6 }, // 13
1266 { 5, 0, "B 2 1 1" , 0, "a1", "B 2x" , 2 }, // 14
1267 { 5, 0, "C 2 1 1" , 0, "a2", "C 2x" , 7 }, // 15
1268 { 5, 0, "I 2 1 1" , 0, "a3", "I 2x" , 8 }, // 16
1269 { 6, 6, "P 1 m 1" , 0, "b", "P -2y" , 0 }, // 17
1270 { 6, 1006, "P 1 1 m" , 0, "c", "P -2" , 1 }, // 18
1271 { 6, 0, "P m 1 1" , 0, "a", "P -2x" , 2 }, // 19
1272 { 7, 7, "P 1 c 1" , 0, "b1", "P -2yc" , 0 }, // 20
1273 { 7, 0, "P 1 n 1" , 0, "b2", "P -2yac" , 9 }, // 21
1274 { 7, 0, "P 1 a 1" , 0, "b3", "P -2ya" , 3 }, // 22
1275 { 7, 0, "P 1 1 a" , 0, "c1", "P -2a" , 1 }, // 23
1276 { 7, 0, "P 1 1 n" , 0, "c2", "P -2ab" , 10}, // 24
1277 { 7, 1007, "P 1 1 b" , 0, "c3", "P -2b" , 5 }, // 25
1278 { 7, 0, "P b 1 1" , 0, "a1", "P -2xb" , 2 }, // 26
1279 { 7, 0, "P n 1 1" , 0, "a2", "P -2xbc" , 11}, // 27
1280 { 7, 0, "P c 1 1" , 0, "a3", "P -2xc" , 7 }, // 28
1281 { 8, 8, "C 1 m 1" , 0, "b1", "C -2y" , 0 }, // 29
1282 { 8, 0, "A 1 m 1" , 0, "b2", "A -2y" , 3 }, // 30
1283 { 8, 0, "I 1 m 1" , 0, "b3", "I -2y" , 4 }, // 31
1284 { 8, 0, "A 1 1 m" , 0, "c1", "A -2" , 1 }, // 32
1285 { 8, 1008, "B 1 1 m" , 0, "c2", "B -2" , 5 }, // 33
1286 { 8, 0, "I 1 1 m" , 0, "c3", "I -2" , 6 }, // 34
1287 { 8, 0, "B m 1 1" , 0, "a1", "B -2x" , 2 }, // 35
1288 { 8, 0, "C m 1 1" , 0, "a2", "C -2x" , 7 }, // 36
1289 { 8, 0, "I m 1 1" , 0, "a3", "I -2x" , 8 }, // 37
1290 { 9, 9, "C 1 c 1" , 0, "b1", "C -2yc" , 0 }, // 38
1291 { 9, 0, "A 1 n 1" , 0, "b2", "A -2yab" , 12}, // 39
1292 { 9, 0, "I 1 a 1" , 0, "b3", "I -2ya" , 13}, // 40
1293 { 9, 0, "A 1 a 1" , 0, "-b1", "A -2ya" , 3 }, // 41
1294 { 9, 0, "C 1 n 1" , 0, "-b2", "C -2yac" , 14}, // 42
1295 { 9, 0, "I 1 c 1" , 0, "-b3", "I -2yc" , 4 }, // 43
1296 { 9, 0, "A 1 1 a" , 0, "c1", "A -2a" , 1 }, // 44
1297 { 9, 0, "B 1 1 n" , 0, "c2", "B -2ab" , 15}, // 45
1298 { 9, 0, "I 1 1 b" , 0, "c3", "I -2b" , 16}, // 46
1299 { 9, 1009, "B 1 1 b" , 0, "-c1", "B -2b" , 5 }, // 47
1300 { 9, 0, "A 1 1 n" , 0, "-c2", "A -2ab" , 10}, // 48
1301 { 9, 0, "I 1 1 a" , 0, "-c3", "I -2a" , 6 }, // 49
1302 { 9, 0, "B b 1 1" , 0, "a1", "B -2xb" , 2 }, // 50
1303 { 9, 0, "C n 1 1" , 0, "a2", "C -2xac" , 17}, // 51
1304 { 9, 0, "I c 1 1" , 0, "a3", "I -2xc" , 18}, // 52
1305 { 9, 0, "C c 1 1" , 0, "-a1", "C -2xc" , 7 }, // 53
1306 { 9, 0, "B n 1 1" , 0, "-a2", "B -2xab" , 11}, // 54
1307 { 9, 0, "I b 1 1" , 0, "-a3", "I -2xb" , 8 }, // 55
1308 { 10, 10, "P 1 2/m 1" , 0, "b", "-P 2y" , 0 }, // 56
1309 { 10, 1010, "P 1 1 2/m" , 0, "c", "-P 2" , 1 }, // 57
1310 { 10, 0, "P 2/m 1 1" , 0, "a", "-P 2x" , 2 }, // 58
1311 { 11, 11, "P 1 21/m 1", 0, "b", "-P 2yb" , 0 }, // 59
1312 { 11, 1011, "P 1 1 21/m", 0, "c", "-P 2c" , 1 }, // 60
1313 { 11, 0, "P 21/m 1 1", 0, "a", "-P 2xa" , 2 }, // 61
1314 { 12, 12, "C 1 2/m 1" , 0, "b1", "-C 2y" , 0 }, // 62
1315 { 12, 0, "A 1 2/m 1" , 0, "b2", "-A 2y" , 3 }, // 63
1316 { 12, 0, "I 1 2/m 1" , 0, "b3", "-I 2y" , 4 }, // 64
1317 { 12, 0, "A 1 1 2/m" , 0, "c1", "-A 2" , 1 }, // 65
1318 { 12, 1012, "B 1 1 2/m" , 0, "c2", "-B 2" , 5 }, // 66
1319 { 12, 0, "I 1 1 2/m" , 0, "c3", "-I 2" , 6 }, // 67
1320 { 12, 0, "B 2/m 1 1" , 0, "a1", "-B 2x" , 2 }, // 68
1321 { 12, 0, "C 2/m 1 1" , 0, "a2", "-C 2x" , 7 }, // 69
1322 { 12, 0, "I 2/m 1 1" , 0, "a3", "-I 2x" , 8 }, // 70
1323 { 13, 13, "P 1 2/c 1" , 0, "b1", "-P 2yc" , 0 }, // 71
1324 { 13, 0, "P 1 2/n 1" , 0, "b2", "-P 2yac" , 9 }, // 72
1325 { 13, 0, "P 1 2/a 1" , 0, "b3", "-P 2ya" , 3 }, // 73
1326 { 13, 0, "P 1 1 2/a" , 0, "c1", "-P 2a" , 1 }, // 74
1327 { 13, 0, "P 1 1 2/n" , 0, "c2", "-P 2ab" , 10}, // 75
1328 { 13, 1013, "P 1 1 2/b" , 0, "c3", "-P 2b" , 5 }, // 76
1329 { 13, 0, "P 2/b 1 1" , 0, "a1", "-P 2xb" , 2 }, // 77
1330 { 13, 0, "P 2/n 1 1" , 0, "a2", "-P 2xbc" , 11}, // 78
1331 { 13, 0, "P 2/c 1 1" , 0, "a3", "-P 2xc" , 7 }, // 79
1332 { 14, 14, "P 1 21/c 1", 0, "b1", "-P 2ybc" , 0 }, // 80
1333 { 14, 2014, "P 1 21/n 1", 0, "b2", "-P 2yn" , 9 }, // 81
1334 { 14, 3014, "P 1 21/a 1", 0, "b3", "-P 2yab" , 3 }, // 82
1335 { 14, 0, "P 1 1 21/a", 0, "c1", "-P 2ac" , 1 }, // 83
1336 { 14, 0, "P 1 1 21/n", 0, "c2", "-P 2n" , 10}, // 84
1337 { 14, 1014, "P 1 1 21/b", 0, "c3", "-P 2bc" , 5 }, // 85
1338 { 14, 0, "P 21/b 1 1", 0, "a1", "-P 2xab" , 2 }, // 86
1339 { 14, 0, "P 21/n 1 1", 0, "a2", "-P 2xn" , 11}, // 87
1340 { 14, 0, "P 21/c 1 1", 0, "a3", "-P 2xac" , 7 }, // 88
1341 { 15, 15, "C 1 2/c 1" , 0, "b1", "-C 2yc" , 0 }, // 89
1342 { 15, 0, "A 1 2/n 1" , 0, "b2", "-A 2yab" , 12}, // 90
1343 { 15, 0, "I 1 2/a 1" , 0, "b3", "-I 2ya" , 13}, // 91
1344 { 15, 0, "A 1 2/a 1" , 0, "-b1", "-A 2ya" , 3 }, // 92
1345 { 15, 0, "C 1 2/n 1" , 0, "-b2", "-C 2yac" , 19}, // 93
1346 { 15, 0, "I 1 2/c 1" , 0, "-b3", "-I 2yc" , 4 }, // 94
1347 { 15, 0, "A 1 1 2/a" , 0, "c1", "-A 2a" , 1 }, // 95
1348 { 15, 0, "B 1 1 2/n" , 0, "c2", "-B 2ab" , 15}, // 96
1349 { 15, 0, "I 1 1 2/b" , 0, "c3", "-I 2b" , 16}, // 97
1350 { 15, 1015, "B 1 1 2/b" , 0, "-c1", "-B 2b" , 5 }, // 98
1351 { 15, 0, "A 1 1 2/n" , 0, "-c2", "-A 2ab" , 10}, // 99
1352 { 15, 0, "I 1 1 2/a" , 0, "-c3", "-I 2a" , 6 }, // 100
1353 { 15, 0, "B 2/b 1 1" , 0, "a1", "-B 2xb" , 2 }, // 101
1354 { 15, 0, "C 2/n 1 1" , 0, "a2", "-C 2xac" , 17}, // 102
1355 { 15, 0, "I 2/c 1 1" , 0, "a3", "-I 2xc" , 18}, // 103
1356 { 15, 0, "C 2/c 1 1" , 0, "-a1", "-C 2xc" , 7 }, // 104
1357 { 15, 0, "B 2/n 1 1" , 0, "-a2", "-B 2xab" , 11}, // 105
1358 { 15, 0, "I 2/b 1 1" , 0, "-a3", "-I 2xb" , 8 }, // 106
1359 { 16, 16, "P 2 2 2" , 0, "", "P 2 2" , 0 }, // 107
1360 { 17, 17, "P 2 2 21" , 0, "", "P 2c 2" , 0 }, // 108
1361 { 17, 1017, "P 21 2 2" , 0, "cab", "P 2a 2a" , 1 }, // 109
1362 { 17, 2017, "P 2 21 2" , 0, "bca", "P 2 2b" , 2 }, // 110
1363 { 18, 18, "P 21 21 2" , 0, "", "P 2 2ab" , 0 }, // 111
1364 { 18, 3018, "P 2 21 21" , 0, "cab", "P 2bc 2" , 1 }, // 112
1365 { 18, 2018, "P 21 2 21" , 0, "bca", "P 2ac 2ac" , 2 }, // 113
1366 { 19, 19, "P 21 21 21", 0, "", "P 2ac 2ab" , 0 }, // 114
1367 { 20, 20, "C 2 2 21" , 0, "", "C 2c 2" , 0 }, // 115
1368 { 20, 0, "A 21 2 2" , 0, "cab", "A 2a 2a" , 1 }, // 116
1369 { 20, 0, "B 2 21 2" , 0, "bca", "B 2 2b" , 2 }, // 117
1370 { 21, 21, "C 2 2 2" , 0, "", "C 2 2" , 0 }, // 118
1371 { 21, 0, "A 2 2 2" , 0, "cab", "A 2 2" , 1 }, // 119
1372 { 21, 0, "B 2 2 2" , 0, "bca", "B 2 2" , 2 }, // 120
1373 { 22, 22, "F 2 2 2" , 0, "", "F 2 2" , 0 }, // 121
1374 { 23, 23, "I 2 2 2" , 0, "", "I 2 2" , 0 }, // 122
1375 { 24, 24, "I 21 21 21", 0, "", "I 2b 2c" , 0 }, // 123
1376 { 25, 25, "P m m 2" , 0, "", "P 2 -2" , 0 }, // 124
1377 { 25, 0, "P 2 m m" , 0, "cab", "P -2 2" , 1 }, // 125
1378 { 25, 0, "P m 2 m" , 0, "bca", "P -2 -2" , 2 }, // 126
1379 { 26, 26, "P m c 21" , 0, "", "P 2c -2" , 0 }, // 127
1380 { 26, 0, "P c m 21" , 0, "ba-c", "P 2c -2c" , 7 }, // 128
1381 { 26, 0, "P 21 m a" , 0, "cab", "P -2a 2a" , 1 }, // 129
1382 { 26, 0, "P 21 a m" , 0, "-cba", "P -2 2a" , 3 }, // 130
1383 { 26, 0, "P b 21 m" , 0, "bca", "P -2 -2b" , 2 }, // 131
1384 { 26, 0, "P m 21 b" , 0, "a-cb", "P -2b -2" , 5 }, // 132
1385 { 27, 27, "P c c 2" , 0, "", "P 2 -2c" , 0 }, // 133
1386 { 27, 0, "P 2 a a" , 0, "cab", "P -2a 2" , 1 }, // 134
1387 { 27, 0, "P b 2 b" , 0, "bca", "P -2b -2b" , 2 }, // 135
1388 { 28, 28, "P m a 2" , 0, "", "P 2 -2a" , 0 }, // 136
1389 { 28, 0, "P b m 2" , 0, "ba-c", "P 2 -2b" , 7 }, // 137
1390 { 28, 0, "P 2 m b" , 0, "cab", "P -2b 2" , 1 }, // 138
1391 { 28, 0, "P 2 c m" , 0, "-cba", "P -2c 2" , 3 }, // 139
1392 { 28, 0, "P c 2 m" , 0, "bca", "P -2c -2c" , 2 }, // 140
1393 { 28, 0, "P m 2 a" , 0, "a-cb", "P -2a -2a" , 5 }, // 141
1394 { 29, 29, "P c a 21" , 0, "", "P 2c -2ac" , 0 }, // 142
1395 { 29, 0, "P b c 21" , 0, "ba-c", "P 2c -2b" , 7 }, // 143
1396 { 29, 0, "P 21 a b" , 0, "cab", "P -2b 2a" , 1 }, // 144
1397 { 29, 0, "P 21 c a" , 0, "-cba", "P -2ac 2a" , 3 }, // 145
1398 { 29, 0, "P c 21 b" , 0, "bca", "P -2bc -2c" , 2 }, // 146
1399 { 29, 0, "P b 21 a" , 0, "a-cb", "P -2a -2ab" , 5 }, // 147
1400 { 30, 30, "P n c 2" , 0, "", "P 2 -2bc" , 0 }, // 148
1401 { 30, 0, "P c n 2" , 0, "ba-c", "P 2 -2ac" , 7 }, // 149
1402 { 30, 0, "P 2 n a" , 0, "cab", "P -2ac 2" , 1 }, // 150
1403 { 30, 0, "P 2 a n" , 0, "-cba", "P -2ab 2" , 3 }, // 151
1404 { 30, 0, "P b 2 n" , 0, "bca", "P -2ab -2ab" , 2 }, // 152
1405 { 30, 0, "P n 2 b" , 0, "a-cb", "P -2bc -2bc" , 5 }, // 153
1406 { 31, 31, "P m n 21" , 0, "", "P 2ac -2" , 0 }, // 154
1407 { 31, 0, "P n m 21" , 0, "ba-c", "P 2bc -2bc" , 7 }, // 155
1408 { 31, 0, "P 21 m n" , 0, "cab", "P -2ab 2ab" , 1 }, // 156
1409 { 31, 0, "P 21 n m" , 0, "-cba", "P -2 2ac" , 3 }, // 157
1410 { 31, 0, "P n 21 m" , 0, "bca", "P -2 -2bc" , 2 }, // 158
1411 { 31, 0, "P m 21 n" , 0, "a-cb", "P -2ab -2" , 5 }, // 159
1412 { 32, 32, "P b a 2" , 0, "", "P 2 -2ab" , 0 }, // 160
1413 { 32, 0, "P 2 c b" , 0, "cab", "P -2bc 2" , 1 }, // 161
1414 { 32, 0, "P c 2 a" , 0, "bca", "P -2ac -2ac" , 2 }, // 162
1415 { 33, 33, "P n a 21" , 0, "", "P 2c -2n" , 0 }, // 163
1416 { 33, 0, "P b n 21" , 0, "ba-c", "P 2c -2ab" , 7 }, // 164
1417 { 33, 0, "P 21 n b" , 0, "cab", "P -2bc 2a" , 1 }, // 165
1418 { 33, 0, "P 21 c n" , 0, "-cba", "P -2n 2a" , 3 }, // 166
1419 { 33, 0, "P c 21 n" , 0, "bca", "P -2n -2ac" , 2 }, // 167
1420 { 33, 0, "P n 21 a" , 0, "a-cb", "P -2ac -2n" , 5 }, // 168
1421 { 34, 34, "P n n 2" , 0, "", "P 2 -2n" , 0 }, // 169
1422 { 34, 0, "P 2 n n" , 0, "cab", "P -2n 2" , 1 }, // 170
1423 { 34, 0, "P n 2 n" , 0, "bca", "P -2n -2n" , 2 }, // 171
1424 { 35, 35, "C m m 2" , 0, "", "C 2 -2" , 0 }, // 172
1425 { 35, 0, "A 2 m m" , 0, "cab", "A -2 2" , 1 }, // 173
1426 { 35, 0, "B m 2 m" , 0, "bca", "B -2 -2" , 2 }, // 174
1427 { 36, 36, "C m c 21" , 0, "", "C 2c -2" , 0 }, // 175
1428 { 36, 0, "C c m 21" , 0, "ba-c", "C 2c -2c" , 7 }, // 176
1429 { 36, 0, "A 21 m a" , 0, "cab", "A -2a 2a" , 1 }, // 177
1430 { 36, 0, "A 21 a m" , 0, "-cba", "A -2 2a" , 3 }, // 178
1431 { 36, 0, "B b 21 m" , 0, "bca", "B -2 -2b" , 2 }, // 179
1432 { 36, 0, "B m 21 b" , 0, "a-cb", "B -2b -2" , 5 }, // 180
1433 { 37, 37, "C c c 2" , 0, "", "C 2 -2c" , 0 }, // 181
1434 { 37, 0, "A 2 a a" , 0, "cab", "A -2a 2" , 1 }, // 182
1435 { 37, 0, "B b 2 b" , 0, "bca", "B -2b -2b" , 2 }, // 183
1436 { 38, 38, "A m m 2" , 0, "", "A 2 -2" , 0 }, // 184
1437 { 38, 0, "B m m 2" , 0, "ba-c", "B 2 -2" , 7 }, // 185
1438 { 38, 0, "B 2 m m" , 0, "cab", "B -2 2" , 1 }, // 186
1439 { 38, 0, "C 2 m m" , 0, "-cba", "C -2 2" , 3 }, // 187
1440 { 38, 0, "C m 2 m" , 0, "bca", "C -2 -2" , 2 }, // 188
1441 { 38, 0, "A m 2 m" , 0, "a-cb", "A -2 -2" , 5 }, // 189
1442 { 39, 39, "A b m 2" , 0, "", "A 2 -2b" , 0 }, // 190
1443 { 39, 0, "B m a 2" , 0, "ba-c", "B 2 -2a" , 7 }, // 191
1444 { 39, 0, "B 2 c m" , 0, "cab", "B -2a 2" , 1 }, // 192
1445 { 39, 0, "C 2 m b" , 0, "-cba", "C -2a 2" , 3 }, // 193
1446 { 39, 0, "C m 2 a" , 0, "bca", "C -2a -2a" , 2 }, // 194
1447 { 39, 0, "A c 2 m" , 0, "a-cb", "A -2b -2b" , 5 }, // 195
1448 { 40, 40, "A m a 2" , 0, "", "A 2 -2a" , 0 }, // 196
1449 { 40, 0, "B b m 2" , 0, "ba-c", "B 2 -2b" , 7 }, // 197
1450 { 40, 0, "B 2 m b" , 0, "cab", "B -2b 2" , 1 }, // 198
1451 { 40, 0, "C 2 c m" , 0, "-cba", "C -2c 2" , 3 }, // 199
1452 { 40, 0, "C c 2 m" , 0, "bca", "C -2c -2c" , 2 }, // 200
1453 { 40, 0, "A m 2 a" , 0, "a-cb", "A -2a -2a" , 5 }, // 201
1454 { 41, 41, "A b a 2" , 0, "", "A 2 -2ab" , 0 }, // 202
1455 { 41, 0, "B b a 2" , 0, "ba-c", "B 2 -2ab" , 7 }, // 203
1456 { 41, 0, "B 2 c b" , 0, "cab", "B -2ab 2" , 1 }, // 204
1457 { 41, 0, "C 2 c b" , 0, "-cba", "C -2ac 2" , 3 }, // 205
1458 { 41, 0, "C c 2 a" , 0, "bca", "C -2ac -2ac" , 2 }, // 206
1459 { 41, 0, "A c 2 a" , 0, "a-cb", "A -2ab -2ab" , 5 }, // 207
1460 { 42, 42, "F m m 2" , 0, "", "F 2 -2" , 0 }, // 208
1461 { 42, 0, "F 2 m m" , 0, "cab", "F -2 2" , 1 }, // 209
1462 { 42, 0, "F m 2 m" , 0, "bca", "F -2 -2" , 2 }, // 210
1463 { 43, 43, "F d d 2" , 0, "", "F 2 -2d" , 0 }, // 211
1464 { 43, 0, "F 2 d d" , 0, "cab", "F -2d 2" , 1 }, // 212
1465 { 43, 0, "F d 2 d" , 0, "bca", "F -2d -2d" , 2 }, // 213
1466 { 44, 44, "I m m 2" , 0, "", "I 2 -2" , 0 }, // 214
1467 { 44, 0, "I 2 m m" , 0, "cab", "I -2 2" , 1 }, // 215
1468 { 44, 0, "I m 2 m" , 0, "bca", "I -2 -2" , 2 }, // 216
1469 { 45, 45, "I b a 2" , 0, "", "I 2 -2c" , 0 }, // 217
1470 { 45, 0, "I 2 c b" , 0, "cab", "I -2a 2" , 1 }, // 218
1471 { 45, 0, "I c 2 a" , 0, "bca", "I -2b -2b" , 2 }, // 219
1472 { 46, 46, "I m a 2" , 0, "", "I 2 -2a" , 0 }, // 220
1473 { 46, 0, "I b m 2" , 0, "ba-c", "I 2 -2b" , 7 }, // 221
1474 { 46, 0, "I 2 m b" , 0, "cab", "I -2b 2" , 1 }, // 222
1475 { 46, 0, "I 2 c m" , 0, "-cba", "I -2c 2" , 3 }, // 223
1476 { 46, 0, "I c 2 m" , 0, "bca", "I -2c -2c" , 2 }, // 224
1477 { 46, 0, "I m 2 a" , 0, "a-cb", "I -2a -2a" , 5 }, // 225
1478 { 47, 47, "P m m m" , 0, "", "-P 2 2" , 0 }, // 226
1479 { 48, 48, "P n n n" , '1', "", "P 2 2 -1n" , 20}, // 227
1480 { 48, 0, "P n n n" , '2', "", "-P 2ab 2bc" , 0 }, // 228
1481 { 49, 49, "P c c m" , 0, "", "-P 2 2c" , 0 }, // 229
1482 { 49, 0, "P m a a" , 0, "cab", "-P 2a 2" , 1 }, // 230
1483 { 49, 0, "P b m b" , 0, "bca", "-P 2b 2b" , 2 }, // 231
1484 { 50, 50, "P b a n" , '1', "", "P 2 2 -1ab" , 21}, // 232
1485 { 50, 0, "P b a n" , '2', "", "-P 2ab 2b" , 0 }, // 233
1486 { 50, 0, "P n c b" , '1', "cab", "P 2 2 -1bc" , 22}, // 234
1487 { 50, 0, "P n c b" , '2', "cab", "-P 2b 2bc" , 1 }, // 235
1488 { 50, 0, "P c n a" , '1', "bca", "P 2 2 -1ac" , 23}, // 236
1489 { 50, 0, "P c n a" , '2', "bca", "-P 2a 2c" , 2 }, // 237
1490 { 51, 51, "P m m a" , 0, "", "-P 2a 2a" , 0 }, // 238
1491 { 51, 0, "P m m b" , 0, "ba-c", "-P 2b 2" , 7 }, // 239
1492 { 51, 0, "P b m m" , 0, "cab", "-P 2 2b" , 1 }, // 240
1493 { 51, 0, "P c m m" , 0, "-cba", "-P 2c 2c" , 3 }, // 241
1494 { 51, 0, "P m c m" , 0, "bca", "-P 2c 2" , 2 }, // 242
1495 { 51, 0, "P m a m" , 0, "a-cb", "-P 2 2a" , 5 }, // 243
1496 { 52, 52, "P n n a" , 0, "", "-P 2a 2bc" , 0 }, // 244
1497 { 52, 0, "P n n b" , 0, "ba-c", "-P 2b 2n" , 7 }, // 245
1498 { 52, 0, "P b n n" , 0, "cab", "-P 2n 2b" , 1 }, // 246
1499 { 52, 0, "P c n n" , 0, "-cba", "-P 2ab 2c" , 3 }, // 247
1500 { 52, 0, "P n c n" , 0, "bca", "-P 2ab 2n" , 2 }, // 248
1501 { 52, 0, "P n a n" , 0, "a-cb", "-P 2n 2bc" , 5 }, // 249
1502 { 53, 53, "P m n a" , 0, "", "-P 2ac 2" , 0 }, // 250
1503 { 53, 0, "P n m b" , 0, "ba-c", "-P 2bc 2bc" , 7 }, // 251
1504 { 53, 0, "P b m n" , 0, "cab", "-P 2ab 2ab" , 1 }, // 252
1505 { 53, 0, "P c n m" , 0, "-cba", "-P 2 2ac" , 3 }, // 253
1506 { 53, 0, "P n c m" , 0, "bca", "-P 2 2bc" , 2 }, // 254
1507 { 53, 0, "P m a n" , 0, "a-cb", "-P 2ab 2" , 5 }, // 255
1508 { 54, 54, "P c c a" , 0, "", "-P 2a 2ac" , 0 }, // 256
1509 { 54, 0, "P c c b" , 0, "ba-c", "-P 2b 2c" , 7 }, // 257
1510 { 54, 0, "P b a a" , 0, "cab", "-P 2a 2b" , 1 }, // 258
1511 { 54, 0, "P c a a" , 0, "-cba", "-P 2ac 2c" , 3 }, // 259
1512 { 54, 0, "P b c b" , 0, "bca", "-P 2bc 2b" , 2 }, // 260
1513 { 54, 0, "P b a b" , 0, "a-cb", "-P 2b 2ab" , 5 }, // 261
1514 { 55, 55, "P b a m" , 0, "", "-P 2 2ab" , 0 }, // 262
1515 { 55, 0, "P m c b" , 0, "cab", "-P 2bc 2" , 1 }, // 263
1516 { 55, 0, "P c m a" , 0, "bca", "-P 2ac 2ac" , 2 }, // 264
1517 { 56, 56, "P c c n" , 0, "", "-P 2ab 2ac" , 0 }, // 265
1518 { 56, 0, "P n a a" , 0, "cab", "-P 2ac 2bc" , 1 }, // 266
1519 { 56, 0, "P b n b" , 0, "bca", "-P 2bc 2ab" , 2 }, // 267
1520 { 57, 57, "P b c m" , 0, "", "-P 2c 2b" , 0 }, // 268
1521 { 57, 0, "P c a m" , 0, "ba-c", "-P 2c 2ac" , 7 }, // 269
1522 { 57, 0, "P m c a" , 0, "cab", "-P 2ac 2a" , 1 }, // 270
1523 { 57, 0, "P m a b" , 0, "-cba", "-P 2b 2a" , 3 }, // 271
1524 { 57, 0, "P b m a" , 0, "bca", "-P 2a 2ab" , 2 }, // 272
1525 { 57, 0, "P c m b" , 0, "a-cb", "-P 2bc 2c" , 5 }, // 273
1526 { 58, 58, "P n n m" , 0, "", "-P 2 2n" , 0 }, // 274
1527 { 58, 0, "P m n n" , 0, "cab", "-P 2n 2" , 1 }, // 275
1528 { 58, 0, "P n m n" , 0, "bca", "-P 2n 2n" , 2 }, // 276
1529 { 59, 59, "P m m n" , '1', "", "P 2 2ab -1ab" , 21}, // 277
1530 { 59, 1059, "P m m n" , '2', "", "-P 2ab 2a" , 0 }, // 278
1531 { 59, 0, "P n m m" , '1', "cab", "P 2bc 2 -1bc" , 22}, // 279
1532 { 59, 0, "P n m m" , '2', "cab", "-P 2c 2bc" , 1 }, // 280
1533 { 59, 0, "P m n m" , '1', "bca", "P 2ac 2ac -1ac", 23}, // 281
1534 { 59, 0, "P m n m" , '2', "bca", "-P 2c 2a" , 2 }, // 282
1535 { 60, 60, "P b c n" , 0, "", "-P 2n 2ab" , 0 }, // 283
1536 { 60, 0, "P c a n" , 0, "ba-c", "-P 2n 2c" , 7 }, // 284
1537 { 60, 0, "P n c a" , 0, "cab", "-P 2a 2n" , 1 }, // 285
1538 { 60, 0, "P n a b" , 0, "-cba", "-P 2bc 2n" , 3 }, // 286
1539 { 60, 0, "P b n a" , 0, "bca", "-P 2ac 2b" , 2 }, // 287
1540 { 60, 0, "P c n b" , 0, "a-cb", "-P 2b 2ac" , 5 }, // 288
1541 { 61, 61, "P b c a" , 0, "", "-P 2ac 2ab" , 0 }, // 289
1542 { 61, 0, "P c a b" , 0, "ba-c", "-P 2bc 2ac" , 3 }, // 290
1543 { 62, 62, "P n m a" , 0, "", "-P 2ac 2n" , 0 }, // 291
1544 { 62, 0, "P m n b" , 0, "ba-c", "-P 2bc 2a" , 7 }, // 292
1545 { 62, 0, "P b n m" , 0, "cab", "-P 2c 2ab" , 1 }, // 293
1546 { 62, 0, "P c m n" , 0, "-cba", "-P 2n 2ac" , 3 }, // 294
1547 { 62, 0, "P m c n" , 0, "bca", "-P 2n 2a" , 2 }, // 295
1548 { 62, 0, "P n a m" , 0, "a-cb", "-P 2c 2n" , 5 }, // 296
1549 { 63, 63, "C m c m" , 0, "", "-C 2c 2" , 0 }, // 297
1550 { 63, 0, "C c m m" , 0, "ba-c", "-C 2c 2c" , 7 }, // 298
1551 { 63, 0, "A m m a" , 0, "cab", "-A 2a 2a" , 1 }, // 299
1552 { 63, 0, "A m a m" , 0, "-cba", "-A 2 2a" , 3 }, // 300
1553 { 63, 0, "B b m m" , 0, "bca", "-B 2 2b" , 2 }, // 301
1554 { 63, 0, "B m m b" , 0, "a-cb", "-B 2b 2" , 5 }, // 302
1555 { 64, 64, "C m c a" , 0, "", "-C 2ac 2" , 0 }, // 303
1556 { 64, 0, "C c m b" , 0, "ba-c", "-C 2ac 2ac" , 7 }, // 304
1557 { 64, 0, "A b m a" , 0, "cab", "-A 2ab 2ab" , 1 }, // 305
1558 { 64, 0, "A c a m" , 0, "-cba", "-A 2 2ab" , 3 }, // 306
1559 { 64, 0, "B b c m" , 0, "bca", "-B 2 2ab" , 2 }, // 307
1560 { 64, 0, "B m a b" , 0, "a-cb", "-B 2ab 2" , 5 }, // 308
1561 { 65, 65, "C m m m" , 0, "", "-C 2 2" , 0 }, // 309
1562 { 65, 0, "A m m m" , 0, "cab", "-A 2 2" , 1 }, // 310
1563 { 65, 0, "B m m m" , 0, "bca", "-B 2 2" , 2 }, // 311
1564 { 66, 66, "C c c m" , 0, "", "-C 2 2c" , 0 }, // 312
1565 { 66, 0, "A m a a" , 0, "cab", "-A 2a 2" , 1 }, // 313
1566 { 66, 0, "B b m b" , 0, "bca", "-B 2b 2b" , 2 }, // 314
1567 { 67, 67, "C m m a" , 0, "", "-C 2a 2" , 0 }, // 315
1568 { 67, 0, "C m m b" , 0, "ba-c", "-C 2a 2a" , 14}, // 316
1569 { 67, 0, "A b m m" , 0, "cab", "-A 2b 2b" , 1 }, // 317
1570 { 67, 0, "A c m m" , 0, "-cba", "-A 2 2b" , 3 }, // 318
1571 { 67, 0, "B m c m" , 0, "bca", "-B 2 2a" , 2 }, // 319
1572 { 67, 0, "B m a m" , 0, "a-cb", "-B 2a 2" , 5 }, // 320
1573 { 68, 68, "C c c a" , '1', "", "C 2 2 -1ac" , 24}, // 321
1574 { 68, 0, "C c c a" , '2', "", "-C 2a 2ac" , 0 }, // 322
1575 { 68, 0, "C c c b" , '1', "ba-c", "C 2 2 -1ac" , 24}, // 323 (==321)
1576 { 68, 0, "C c c b" , '2', "ba-c", "-C 2a 2c" , 21}, // 324
1577 { 68, 0, "A b a a" , '1', "cab", "A 2 2 -1ab" , 25}, // 325
1578 { 68, 0, "A b a a" , '2', "cab", "-A 2a 2b" , 1 }, // 326
1579 { 68, 0, "A c a a" , '1', "-cba", "A 2 2 -1ab" , 25}, // 327 (==325)
1580 { 68, 0, "A c a a" , '2', "-cba", "-A 2ab 2b" , 3 }, // 328
1581 { 68, 0, "B b c b" , '1', "bca", "B 2 2 -1ab" , 26}, // 329
1582 { 68, 0, "B b c b" , '2', "bca", "-B 2ab 2b" , 2 }, // 330
1583 { 68, 0, "B b a b" , '1', "a-cb", "B 2 2 -1ab" , 26}, // 331 (==329)
1584 { 68, 0, "B b a b" , '2', "a-cb", "-B 2b 2ab" , 5 }, // 332
1585 { 69, 69, "F m m m" , 0, "", "-F 2 2" , 0 }, // 333
1586 { 70, 70, "F d d d" , '1', "", "F 2 2 -1d" , 27}, // 334
1587 { 70, 0, "F d d d" , '2', "", "-F 2uv 2vw" , 0 }, // 335
1588 { 71, 71, "I m m m" , 0, "", "-I 2 2" , 0 }, // 336
1589 { 72, 72, "I b a m" , 0, "", "-I 2 2c" , 0 }, // 337
1590 { 72, 0, "I m c b" , 0, "cab", "-I 2a 2" , 1 }, // 338
1591 { 72, 0, "I c m a" , 0, "bca", "-I 2b 2b" , 2 }, // 339
1592 { 73, 73, "I b c a" , 0, "", "-I 2b 2c" , 0 }, // 340
1593 { 73, 0, "I c a b" , 0, "ba-c", "-I 2a 2b" , 28}, // 341
1594 { 74, 74, "I m m a" , 0, "", "-I 2b 2" , 0 }, // 342
1595 { 74, 0, "I m m b" , 0, "ba-c", "-I 2a 2a" , 28}, // 343
1596 { 74, 0, "I b m m" , 0, "cab", "-I 2c 2c" , 1 }, // 344
1597 { 74, 0, "I c m m" , 0, "-cba", "-I 2 2b" , 3 }, // 345
1598 { 74, 0, "I m c m" , 0, "bca", "-I 2 2a" , 2 }, // 346
1599 { 74, 0, "I m a m" , 0, "a-cb", "-I 2c 2" , 5 }, // 347
1600 { 75, 75, "P 4" , 0, "", "P 4" , 0 }, // 348
1601 { 76, 76, "P 41" , 0, "", "P 4w" , 0 }, // 349
1602 { 77, 77, "P 42" , 0, "", "P 4c" , 0 }, // 350
1603 { 78, 78, "P 43" , 0, "", "P 4cw" , 0 }, // 351
1604 { 79, 79, "I 4" , 0, "", "I 4" , 0 }, // 352
1605 { 80, 80, "I 41" , 0, "", "I 4bw" , 0 }, // 353
1606 { 81, 81, "P -4" , 0, "", "P -4" , 0 }, // 354
1607 { 82, 82, "I -4" , 0, "", "I -4" , 0 }, // 355
1608 { 83, 83, "P 4/m" , 0, "", "-P 4" , 0 }, // 356
1609 { 84, 84, "P 42/m" , 0, "", "-P 4c" , 0 }, // 357
1610 { 85, 85, "P 4/n" , '1', "", "P 4ab -1ab" , 29}, // 358
1611 { 85, 0, "P 4/n" , '2', "", "-P 4a" , 0 }, // 359
1612 { 86, 86, "P 42/n" , '1', "", "P 4n -1n" , 30}, // 360
1613 { 86, 0, "P 42/n" , '2', "", "-P 4bc" , 0 }, // 361
1614 { 87, 87, "I 4/m" , 0, "", "-I 4" , 0 }, // 362
1615 { 88, 88, "I 41/a" , '1', "", "I 4bw -1bw" , 31}, // 363
1616 { 88, 0, "I 41/a" , '2', "", "-I 4ad" , 0 }, // 364
1617 { 89, 89, "P 4 2 2" , 0, "", "P 4 2" , 0 }, // 365
1618 { 90, 90, "P 4 21 2" , 0, "", "P 4ab 2ab" , 0 }, // 366
1619 { 91, 91, "P 41 2 2" , 0, "", "P 4w 2c" , 0 }, // 367
1620 { 92, 92, "P 41 21 2" , 0, "", "P 4abw 2nw" , 0 }, // 368
1621 { 93, 93, "P 42 2 2" , 0, "", "P 4c 2" , 0 }, // 369
1622 { 94, 94, "P 42 21 2" , 0, "", "P 4n 2n" , 0 }, // 370
1623 { 95, 95, "P 43 2 2" , 0, "", "P 4cw 2c" , 0 }, // 371
1624 { 96, 96, "P 43 21 2" , 0, "", "P 4nw 2abw" , 0 }, // 372
1625 { 97, 97, "I 4 2 2" , 0, "", "I 4 2" , 0 }, // 373
1626 { 98, 98, "I 41 2 2" , 0, "", "I 4bw 2bw" , 0 }, // 374
1627 { 99, 99, "P 4 m m" , 0, "", "P 4 -2" , 0 }, // 375
1628 {100, 100, "P 4 b m" , 0, "", "P 4 -2ab" , 0 }, // 376
1629 {101, 101, "P 42 c m" , 0, "", "P 4c -2c" , 0 }, // 377
1630 {102, 102, "P 42 n m" , 0, "", "P 4n -2n" , 0 }, // 378
1631 {103, 103, "P 4 c c" , 0, "", "P 4 -2c" , 0 }, // 379
1632 {104, 104, "P 4 n c" , 0, "", "P 4 -2n" , 0 }, // 380
1633 {105, 105, "P 42 m c" , 0, "", "P 4c -2" , 0 }, // 381
1634 {106, 106, "P 42 b c" , 0, "", "P 4c -2ab" , 0 }, // 382
1635 {107, 107, "I 4 m m" , 0, "", "I 4 -2" , 0 }, // 383
1636 {108, 108, "I 4 c m" , 0, "", "I 4 -2c" , 0 }, // 384
1637 {109, 109, "I 41 m d" , 0, "", "I 4bw -2" , 0 }, // 385
1638 {110, 110, "I 41 c d" , 0, "", "I 4bw -2c" , 0 }, // 386
1639 {111, 111, "P -4 2 m" , 0, "", "P -4 2" , 0 }, // 387
1640 {112, 112, "P -4 2 c" , 0, "", "P -4 2c" , 0 }, // 388
1641 {113, 113, "P -4 21 m" , 0, "", "P -4 2ab" , 0 }, // 389
1642 {114, 114, "P -4 21 c" , 0, "", "P -4 2n" , 0 }, // 390
1643 {115, 115, "P -4 m 2" , 0, "", "P -4 -2" , 0 }, // 391
1644 {116, 116, "P -4 c 2" , 0, "", "P -4 -2c" , 0 }, // 392
1645 {117, 117, "P -4 b 2" , 0, "", "P -4 -2ab" , 0 }, // 393
1646 {118, 118, "P -4 n 2" , 0, "", "P -4 -2n" , 0 }, // 394
1647 {119, 119, "I -4 m 2" , 0, "", "I -4 -2" , 0 }, // 395
1648 {120, 120, "I -4 c 2" , 0, "", "I -4 -2c" , 0 }, // 396
1649 {121, 121, "I -4 2 m" , 0, "", "I -4 2" , 0 }, // 397
1650 {122, 122, "I -4 2 d" , 0, "", "I -4 2bw" , 0 }, // 398
1651 {123, 123, "P 4/m m m" , 0, "", "-P 4 2" , 0 }, // 399
1652 {124, 124, "P 4/m c c" , 0, "", "-P 4 2c" , 0 }, // 400
1653 {125, 125, "P 4/n b m" , '1', "", "P 4 2 -1ab" , 21}, // 401
1654 {125, 0, "P 4/n b m" , '2', "", "-P 4a 2b" , 0 }, // 402
1655 {126, 126, "P 4/n n c" , '1', "", "P 4 2 -1n" , 20}, // 403
1656 {126, 0, "P 4/n n c" , '2', "", "-P 4a 2bc" , 0 }, // 404
1657 {127, 127, "P 4/m b m" , 0, "", "-P 4 2ab" , 0 }, // 405
1658 {128, 128, "P 4/m n c" , 0, "", "-P 4 2n" , 0 }, // 406
1659 {129, 129, "P 4/n m m" , '1', "", "P 4ab 2ab -1ab", 29}, // 407
1660 {129, 0, "P 4/n m m" , '2', "", "-P 4a 2a" , 0 }, // 408
1661 {130, 130, "P 4/n c c" , '1', "", "P 4ab 2n -1ab" , 29}, // 409
1662 {130, 0, "P 4/n c c" , '2', "", "-P 4a 2ac" , 0 }, // 410
1663 {131, 131, "P 42/m m c", 0, "", "-P 4c 2" , 0 }, // 411
1664 {132, 132, "P 42/m c m", 0, "", "-P 4c 2c" , 0 }, // 412
1665 {133, 133, "P 42/n b c", '1', "", "P 4n 2c -1n" , 32}, // 413
1666 {133, 0, "P 42/n b c", '2', "", "-P 4ac 2b" , 0 }, // 414
1667 {134, 134, "P 42/n n m", '1', "", "P 4n 2 -1n" , 33}, // 415
1668 {134, 0, "P 42/n n m", '2', "", "-P 4ac 2bc" , 0 }, // 416
1669 {135, 135, "P 42/m b c", 0, "", "-P 4c 2ab" , 0 }, // 417
1670 {136, 136, "P 42/m n m", 0, "", "-P 4n 2n" , 0 }, // 418
1671 {137, 137, "P 42/n m c", '1', "", "P 4n 2n -1n" , 32}, // 419
1672 {137, 0, "P 42/n m c", '2', "", "-P 4ac 2a" , 0 }, // 420
1673 {138, 138, "P 42/n c m", '1', "", "P 4n 2ab -1n" , 33}, // 421
1674 {138, 0, "P 42/n c m", '2', "", "-P 4ac 2ac" , 0 }, // 422
1675 {139, 139, "I 4/m m m" , 0, "", "-I 4 2" , 0 }, // 423
1676 {140, 140, "I 4/m c m" , 0, "", "-I 4 2c" , 0 }, // 424
1677 {141, 141, "I 41/a m d", '1', "", "I 4bw 2bw -1bw", 34}, // 425
1678 {141, 0, "I 41/a m d", '2', "", "-I 4bd 2" , 0 }, // 426
1679 {142, 142, "I 41/a c d", '1', "", "I 4bw 2aw -1bw", 35}, // 427
1680 {142, 0, "I 41/a c d", '2', "", "-I 4bd 2c" , 0 }, // 428
1681 {143, 143, "P 3" , 0, "", "P 3" , 0 }, // 429
1682 {144, 144, "P 31" , 0, "", "P 31" , 0 }, // 430
1683 {145, 145, "P 32" , 0, "", "P 32" , 0 }, // 431
1684 {146, 146, "R 3" , 'H', "", "R 3" , 0 }, // 432
1685 {146, 1146, "R 3" , 'R', "", "P 3*" , 36}, // 433
1686 {147, 147, "P -3" , 0, "", "-P 3" , 0 }, // 434
1687 {148, 148, "R -3" , 'H', "", "-R 3" , 0 }, // 435
1688 {148, 1148, "R -3" , 'R', "", "-P 3*" , 36}, // 436
1689 {149, 149, "P 3 1 2" , 0, "", "P 3 2" , 0 }, // 437
1690 {150, 150, "P 3 2 1" , 0, "", "P 3 2\"" , 0 }, // 438
1691 {151, 151, "P 31 1 2" , 0, "", "P 31 2 (0 0 4)", 0 }, // 439
1692 {152, 152, "P 31 2 1" , 0, "", "P 31 2\"" , 0 }, // 440
1693 {153, 153, "P 32 1 2" , 0, "", "P 32 2 (0 0 2)", 0 }, // 441
1694 {154, 154, "P 32 2 1" , 0, "", "P 32 2\"" , 0 }, // 442
1695 {155, 155, "R 3 2" , 'H', "", "R 3 2\"" , 0 }, // 443
1696 {155, 1155, "R 3 2" , 'R', "", "P 3* 2" , 36}, // 444
1697 {156, 156, "P 3 m 1" , 0, "", "P 3 -2\"" , 0 }, // 445
1698 {157, 157, "P 3 1 m" , 0, "", "P 3 -2" , 0 }, // 446
1699 {158, 158, "P 3 c 1" , 0, "", "P 3 -2\"c" , 0 }, // 447
1700 {159, 159, "P 3 1 c" , 0, "", "P 3 -2c" , 0 }, // 448
1701 {160, 160, "R 3 m" , 'H', "", "R 3 -2\"" , 0 }, // 449
1702 {160, 1160, "R 3 m" , 'R', "", "P 3* -2" , 36}, // 450
1703 {161, 161, "R 3 c" , 'H', "", "R 3 -2\"c" , 0 }, // 451
1704 {161, 1161, "R 3 c" , 'R', "", "P 3* -2n" , 36}, // 452
1705 {162, 162, "P -3 1 m" , 0, "", "-P 3 2" , 0 }, // 453
1706 {163, 163, "P -3 1 c" , 0, "", "-P 3 2c" , 0 }, // 454
1707 {164, 164, "P -3 m 1" , 0, "", "-P 3 2\"" , 0 }, // 455
1708 {165, 165, "P -3 c 1" , 0, "", "-P 3 2\"c" , 0 }, // 456
1709 {166, 166, "R -3 m" , 'H', "", "-R 3 2\"" , 0 }, // 457
1710 {166, 1166, "R -3 m" , 'R', "", "-P 3* 2" , 36}, // 458
1711 {167, 167, "R -3 c" , 'H', "", "-R 3 2\"c" , 0 }, // 459
1712 {167, 1167, "R -3 c" , 'R', "", "-P 3* 2n" , 36}, // 460
1713 {168, 168, "P 6" , 0, "", "P 6" , 0 }, // 461
1714 {169, 169, "P 61" , 0, "", "P 61" , 0 }, // 462
1715 {170, 170, "P 65" , 0, "", "P 65" , 0 }, // 463
1716 {171, 171, "P 62" , 0, "", "P 62" , 0 }, // 464
1717 {172, 172, "P 64" , 0, "", "P 64" , 0 }, // 465
1718 {173, 173, "P 63" , 0, "", "P 6c" , 0 }, // 466
1719 {174, 174, "P -6" , 0, "", "P -6" , 0 }, // 467
1720 {175, 175, "P 6/m" , 0, "", "-P 6" , 0 }, // 468
1721 {176, 176, "P 63/m" , 0, "", "-P 6c" , 0 }, // 469
1722 {177, 177, "P 6 2 2" , 0, "", "P 6 2" , 0 }, // 470
1723 {178, 178, "P 61 2 2" , 0, "", "P 61 2 (0 0 5)", 0 }, // 471
1724 {179, 179, "P 65 2 2" , 0, "", "P 65 2 (0 0 1)", 0 }, // 472
1725 {180, 180, "P 62 2 2" , 0, "", "P 62 2 (0 0 4)", 0 }, // 473
1726 {181, 181, "P 64 2 2" , 0, "", "P 64 2 (0 0 2)", 0 }, // 474
1727 {182, 182, "P 63 2 2" , 0, "", "P 6c 2c" , 0 }, // 475
1728 {183, 183, "P 6 m m" , 0, "", "P 6 -2" , 0 }, // 476
1729 {184, 184, "P 6 c c" , 0, "", "P 6 -2c" , 0 }, // 477
1730 {185, 185, "P 63 c m" , 0, "", "P 6c -2" , 0 }, // 478
1731 {186, 186, "P 63 m c" , 0, "", "P 6c -2c" , 0 }, // 479
1732 {187, 187, "P -6 m 2" , 0, "", "P -6 2" , 0 }, // 480
1733 {188, 188, "P -6 c 2" , 0, "", "P -6c 2" , 0 }, // 481
1734 {189, 189, "P -6 2 m" , 0, "", "P -6 -2" , 0 }, // 482
1735 {190, 190, "P -6 2 c" , 0, "", "P -6c -2c" , 0 }, // 483
1736 {191, 191, "P 6/m m m" , 0, "", "-P 6 2" , 0 }, // 484
1737 {192, 192, "P 6/m c c" , 0, "", "-P 6 2c" , 0 }, // 485
1738 {193, 193, "P 63/m c m", 0, "", "-P 6c 2" , 0 }, // 486
1739 {194, 194, "P 63/m m c", 0, "", "-P 6c 2c" , 0 }, // 487
1740 {195, 195, "P 2 3" , 0, "", "P 2 2 3" , 0 }, // 488
1741 {196, 196, "F 2 3" , 0, "", "F 2 2 3" , 0 }, // 489
1742 {197, 197, "I 2 3" , 0, "", "I 2 2 3" , 0 }, // 490
1743 {198, 198, "P 21 3" , 0, "", "P 2ac 2ab 3" , 0 }, // 491
1744 {199, 199, "I 21 3" , 0, "", "I 2b 2c 3" , 0 }, // 492
1745 {200, 200, "P m -3" , 0, "", "-P 2 2 3" , 0 }, // 493
1746 {201, 201, "P n -3" , '1', "", "P 2 2 3 -1n" , 20}, // 494
1747 {201, 0, "P n -3" , '2', "", "-P 2ab 2bc 3" , 0 }, // 495
1748 {202, 202, "F m -3" , 0, "", "-F 2 2 3" , 0 }, // 496
1749 {203, 203, "F d -3" , '1', "", "F 2 2 3 -1d" , 27}, // 497
1750 {203, 0, "F d -3" , '2', "", "-F 2uv 2vw 3" , 0 }, // 498
1751 {204, 204, "I m -3" , 0, "", "-I 2 2 3" , 0 }, // 499
1752 {205, 205, "P a -3" , 0, "", "-P 2ac 2ab 3" , 0 }, // 500
1753 {206, 206, "I a -3" , 0, "", "-I 2b 2c 3" , 0 }, // 501
1754 {207, 207, "P 4 3 2" , 0, "", "P 4 2 3" , 0 }, // 502
1755 {208, 208, "P 42 3 2" , 0, "", "P 4n 2 3" , 0 }, // 503
1756 {209, 209, "F 4 3 2" , 0, "", "F 4 2 3" , 0 }, // 504
1757 {210, 210, "F 41 3 2" , 0, "", "F 4d 2 3" , 0 }, // 505
1758 {211, 211, "I 4 3 2" , 0, "", "I 4 2 3" , 0 }, // 506
1759 {212, 212, "P 43 3 2" , 0, "", "P 4acd 2ab 3" , 0 }, // 507
1760 {213, 213, "P 41 3 2" , 0, "", "P 4bd 2ab 3" , 0 }, // 508
1761 {214, 214, "I 41 3 2" , 0, "", "I 4bd 2c 3" , 0 }, // 509
1762 {215, 215, "P -4 3 m" , 0, "", "P -4 2 3" , 0 }, // 510
1763 {216, 216, "F -4 3 m" , 0, "", "F -4 2 3" , 0 }, // 511
1764 {217, 217, "I -4 3 m" , 0, "", "I -4 2 3" , 0 }, // 512
1765 {218, 218, "P -4 3 n" , 0, "", "P -4n 2 3" , 0 }, // 513
1766 {219, 219, "F -4 3 c" , 0, "", "F -4a 2 3" , 0 }, // 514
1767 {220, 220, "I -4 3 d" , 0, "", "I -4bd 2c 3" , 0 }, // 515
1768 {221, 221, "P m -3 m" , 0, "", "-P 4 2 3" , 0 }, // 516
1769 {222, 222, "P n -3 n" , '1', "", "P 4 2 3 -1n" , 20}, // 517
1770 {222, 0, "P n -3 n" , '2', "", "-P 4a 2bc 3" , 0 }, // 518
1771 {223, 223, "P m -3 n" , 0, "", "-P 4n 2 3" , 0 }, // 519
1772 {224, 224, "P n -3 m" , '1', "", "P 4n 2 3 -1n" , 30}, // 520
1773 {224, 0, "P n -3 m" , '2', "", "-P 4bc 2bc 3" , 0 }, // 521
1774 {225, 225, "F m -3 m" , 0, "", "-F 4 2 3" , 0 }, // 522
1775 {226, 226, "F m -3 c" , 0, "", "-F 4a 2 3" , 0 }, // 523
1776 {227, 227, "F d -3 m" , '1', "", "F 4d 2 3 -1d" , 27}, // 524
1777 {227, 0, "F d -3 m" , '2', "", "-F 4vw 2vw 3" , 0 }, // 525
1778 {228, 228, "F d -3 c" , '1', "", "F 4d 2 3 -1ad" , 37}, // 526
1779 {228, 0, "F d -3 c" , '2', "", "-F 4ud 2vw 3" , 0 }, // 527
1780 {229, 229, "I m -3 m" , 0, "", "-I 4 2 3" , 0 }, // 528
1781 {230, 230, "I a -3 d" , 0, "", "-I 4bd 2c 3" , 0 }, // 529
1782 // And extra entries from syminfo.lib
1783 { 5, 5005, "I 1 21 1" , 0, "b4", "I 2yb" , 38}, // 530
1784 { 5, 3005, "C 1 21 1" , 0, "b5", "C 2yb" , 14}, // 531
1785 { 18, 1018, "P 21212(a)", 0, "", "P 2ab 2a" , 14}, // 532
1786 { 20, 1020, "C 2 2 21a)", 0, "", "C 2ac 2" , 39}, // 533
1787 { 21, 1021, "C 2 2 2a" , 0, "", "C 2ab 2b" , 14}, // 534
1788 { 22, 1022, "F 2 2 2a" , 0, "", "F 2 2c" , 40}, // 535
1789 { 23, 1023, "I 2 2 2a" , 0, "", "I 2ab 2bc" , 33}, // 536
1790 { 94, 1094, "P 42 21 2a", 0, "", "P 4bc 2a" , 20}, // 537
1791 {197, 1197, "I 2 3a" , 0, "", "I 2ab 2bc 3" , 30}, // 538
1792 // And extra entries from Crystallographic Space Group Diagrams and Tables
1793 // http://img.chem.ucl.ac.uk/sgp/
1794 // We want to have all entries from Open Babel and PDB.
1795 // If available, Hall symbols are taken from
1796 // https://cci.lbl.gov/cctbx/multiple_cell.html
1797 // triclinic - enlarged unit cells
1798 { 1, 0, "A 1" , 0, "", "A 1" , 41}, // 539
1799 { 1, 0, "B 1" , 0, "", "B 1" , 42}, // 540
1800 { 1, 0, "C 1" , 0, "", "C 1" , 43}, // 541
1801 { 1, 0, "F 1" , 0, "", "F 1" , 44}, // 542
1802 { 1, 0, "I 1" , 0, "", "I 1" , 45}, // 543
1803 { 2, 0, "A -1" , 0, "", "-A 1" , 41}, // 544
1804 { 2, 0, "B -1" , 0, "", "-B 1" , 42}, // 545
1805 { 2, 0, "C -1" , 0, "", "-C 1" , 43}, // 546
1806 { 2, 0, "F -1" , 0, "", "-F 1" , 44}, // 547
1807 { 2, 0, "I -1" , 0, "", "-I 1" , 45}, // 548
1808 // monoclinic
1809 { 3, 0, "C 1 1 2" , 0, "c1", "C 2" , 46}, // 549
1810 { 4, 0, "C 1 1 21" , 0, "c1", "C 2c" , 46}, // 550
1811 { 12, 0, "F 1 2/m 1" , 0, "b4", "-F 2y" , 47}, // 551
1812 // orthorhombic
1813 { 64, 0, "A b a m" , 0, "", "-A 2 2ab" , 3 }, // 552 (==306)
1814 // tetragonal - enlarged C- and F-centred unit cells
1815 { 89, 0, "C 4 2 2" , 0, "", "C 4 2" , 48}, // 553
1816 { 90, 0, "C 4 2 21" , 0, "", "C 4a 2" , 48}, // 554
1817 { 97, 0, "F 4 2 2" , 0, "", "F 4 2" , 48}, // 555
1818 {115, 0, "C -4 2 m" , 0, "", "C -4 2" , 48}, // 556
1819 {117, 0, "C -4 2 b" , 0, "", "C -4 2ya" , 48}, // 557
1820 {139, 0, "F 4/m m m" , 0, "", "-F 4 2" , 48}, // 558
1821};
1822
1823template<class Dummy>
1824const SpaceGroupAltName Tables_<Dummy>::alt_names[28] = {
1825 // In 1990's ITfC vol.A changed some of the standard names, introducing
1826 // symbols 'e' and 'g'. sgtbx interprets these new symbols with
1827 // option ad_hoc_1992. spglib uses only the new symbols.
1828 {"A e m 2", 0, 190}, // A b m 2
1829 {"B m e 2", 0, 191}, // B m a 2
1830 {"B 2 e m", 0, 192}, // B 2 c m
1831 {"C 2 m e", 0, 193}, // C 2 m b
1832 {"C m 2 e", 0, 194}, // C m 2 a
1833 {"A e 2 m", 0, 195}, // A c 2 m
1834 {"A e a 2", 0, 202}, // A b a 2
1835 {"B b e 2", 0, 203}, // B b a 2
1836 {"B 2 e b", 0, 204}, // B 2 c b
1837 {"C 2 c e", 0, 205}, // C 2 c b
1838 {"C c 2 e", 0, 206}, // C c 2 a
1839 {"A e 2 a", 0, 207}, // A c 2 a
1840 {"C m c e", 0, 303}, // C m c a
1841 {"C c m e", 0, 304}, // C c m b
1842 {"A e m a", 0, 305}, // A b m a
1843 {"A e a m", 0, 306}, // A c a m
1844 {"B b e m", 0, 307}, // B b c m
1845 {"B m e b", 0, 308}, // B m a b
1846 {"C m m e", 0, 315}, // C m m a
1847 {"A e m m", 0, 317}, // A b m m
1848 {"B m e m", 0, 319}, // B m c m
1849 {"C c c e", '1', 321}, // C c c a
1850 {"C c c e", '2', 322}, // C c c a
1851 {"A e a a", '1', 325}, // A b a a
1852 {"A e a a", '2', 326}, // A b a a
1853 {"B b e b", '1', 329}, // B b c b
1854 {"B b e b", '2', 330}, // B b c b
1855 // help with parsing of unusual setting names that are present in the PDB
1856 {"P 21 21 2a", 0, 532}, // P 21212(a)
1857};
1858
1859
1860// This table was generated by tools/gen_reciprocal_asu.py.
1861template<class Dummy>
1862const unsigned char Tables_<Dummy>::ccp4_hkl_asu[230] = {
1863 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1864 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1865 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
1866 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
1867 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
1868 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 7, 6, 7, 6, 7, 7, 7,
1869 6, 7, 6, 7, 7, 6, 6, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
1870 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9,
1871 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
1872};
1873
1874} // namespace impl
1875
1876using spacegroup_tables = impl::Tables_<void>;
1877
1878inline const SpaceGroup* find_spacegroup_by_number(int ccp4) noexcept {
1879 if (ccp4 == 0)
1880 return &spacegroup_tables::main[0];
1881 for (const SpaceGroup& sg : spacegroup_tables::main)
1882 if (sg.ccp4 == ccp4)
1883 return &sg;
1884 return nullptr;
1885}
1886
1887inline const SpaceGroup& get_spacegroup_by_number(int ccp4) {
1889 if (sg == nullptr)
1890 throw std::invalid_argument("Invalid space-group number: "
1891 + std::to_string(ccp4));
1892 return *sg;
1893}
1894
1896 for (const SpaceGroup& sg : spacegroup_tables::main)
1897 if (sg.number == number && sg.is_reference_setting())
1898 return sg;
1899 throw std::invalid_argument("Invalid space-group number: "
1900 + std::to_string(number));
1901}
1902
1903// the angles alpha and gamma are optional. If provided they are only used
1904// to distinguish hexagonal and rhombohedral settings (e.g. for "R 3").
1905inline const SpaceGroup* find_spacegroup_by_name(std::string name,
1906 double alpha=0., double gamma=0.) noexcept {
1907 const char* p = impl::skip_blank(name.c_str());
1908 if (*p >= '0' && *p <= '9') { // handle numbers
1909 char *endptr;
1910 long n = std::strtol(p, &endptr, 10);
1911 return *endptr == '\0' ? find_spacegroup_by_number(n) : nullptr;
1912 }
1913 char first = *p & ~0x20; // to uppercase
1914 if (first == '\0')
1915 return nullptr;
1916 if (first == 'H')
1917 first = 'R';
1918 p = impl::skip_blank(p+1);
1919 size_t start = p - name.c_str();
1920 // change letters to lower case, except the letter after :
1921 for (size_t i = start; i < name.size(); ++i) {
1922 if (name[i] >= 'A' && name[i] <= 'Z')
1923 name[i] |= 0x20; // to lowercase
1924 else if (name[i] == ':')
1925 while (++i < name.size())
1926 if (name[i] >= 'a' && name[i] <= 'z')
1927 name[i] &= ~0x20; // to uppercase
1928 }
1929 // The string that const char* p points to was just modified.
1930 // This confuses some compilers (GCC 4.8), so let's re-assign p.
1931 p = name.c_str() + start;
1932
1933 for (const SpaceGroup& sg : spacegroup_tables::main)
1934 if (sg.hm[0] == first) {
1935 if (sg.hm[2] == *p) {
1936 const char* a = impl::skip_blank(p + 1);
1937 const char* b = impl::skip_blank(sg.hm + 3);
1938 while (*a == *b && *b != '\0') {
1939 a = impl::skip_blank(a+1);
1940 b = impl::skip_blank(b+1);
1941 }
1942 if (*b == '\0' &&
1943 (*a == '\0' || (*a == ':' && *impl::skip_blank(a+1) == sg.ext))) {
1944 // Change hexagonal settings to rhombohedral if the unit cell angles
1945 // are more consistent with the latter.
1946 // We have possible ambiguity in the hexagonal crystal family.
1947 // For instance, "R 3" may mean "R 3:H" (hexagonal setting) or
1948 // "R 3:R" (rhombohedral setting). The :H symbols come first
1949 // in the table and are used by default. The ratio gamma:alpha
1950 // is 120:90 in the hexagonal system and 1:1 in rhombohedral.
1951 // We assume that the 'R' entry follows directly the 'H' entry.
1952 if (*a == '\0' && sg.ext == 'H' && gamma < 1.125 * alpha)
1953 return &sg + 1;
1954 return &sg;
1955 }
1956 } else if (sg.hm[2] == '1' && sg.hm[3] == ' ') {
1957 // check monoclinic short names, matching P2 to "P 1 2 1";
1958 // as an exception "B 2" == "B 1 1 2" (like in the PDB)
1959 const char* b = sg.hm + 4;
1960 if (*b != '1' || (first == 'B' && *++b == ' ' && *++b != '1')) {
1961 char end = (b == sg.hm + 4 ? ' ' : '\0');
1962 const char* a = impl::skip_blank(p);
1963 while (*a == *b && *b != end) {
1964 ++a;
1965 ++b;
1966 }
1967 if (*impl::skip_blank(a) == '\0' && *b == end)
1968 return &sg;
1969 }
1970 }
1971 }
1972 for (const SpaceGroupAltName& sg : spacegroup_tables::alt_names)
1973 if (sg.hm[0] == first && sg.hm[2] == *p) {
1974 const char* a = impl::skip_blank(p + 1);
1975 const char* b = impl::skip_blank(sg.hm + 3);
1976 while (*a == *b && *b != '\0') {
1977 a = impl::skip_blank(a+1);
1978 b = impl::skip_blank(b+1);
1979 }
1980 if (*b == '\0' &&
1981 (*a == '\0' || (*a == ':' && *impl::skip_blank(a+1) == sg.ext)))
1982 return &spacegroup_tables::main[sg.pos];
1983 }
1984 return nullptr;
1985}
1986
1987inline const SpaceGroup& get_spacegroup_by_name(const std::string& name) {
1988 const SpaceGroup* sg = find_spacegroup_by_name(name);
1989 if (sg == nullptr)
1990 throw std::invalid_argument("Unknown space-group name: " + name);
1991 return *sg;
1992}
1993
1995 return spacegroup_tables::main[0];
1996}
1997
1998inline const SpaceGroup* find_spacegroup_by_ops(const GroupOps& gops) {
1999 char c = gops.find_centering();
2000 for (const SpaceGroup& sg : spacegroup_tables::main)
2001 if ((c == sg.hall[0] || c == sg.hall[1]) &&
2002 gops.is_same_as(sg.operations()))
2003 return &sg;
2004 return nullptr;
2005}
2006
2007// Reciprocal space asu (asymmetric unit).
2008// The same 12 choices of ASU as in CCP4 symlib and cctbx.
2010 int idx;
2011 Op::Rot rot{}; // value-initialized only to avoid -Wmaybe-uninitialized
2013
2014 ReciprocalAsu(const SpaceGroup* sg, bool tnt=false) {
2015 if (sg == nullptr)
2016 fail("Missing space group");
2017 idx = spacegroup_tables::ccp4_hkl_asu[sg->number - 1];
2018 if (tnt)
2019 idx += 10;
2020 is_ref = sg->is_reference_setting();
2021 if (!is_ref)
2022 rot = sg->basisop().rot;
2023 }
2024
2025 bool is_in(const Op::Miller& hkl) const {
2026 if (is_ref)
2027 return is_in_reference_setting(hkl[0], hkl[1], hkl[2]);
2028 Op::Miller r;
2029 for (int i = 0; i != 3; ++i)
2030 r[i] = rot[0][i] * hkl[0] + rot[1][i] * hkl[1] + rot[2][i] * hkl[2];
2031 return is_in_reference_setting(r[0], r[1], r[2]);
2032 }
2033
2034 bool is_in_reference_setting(int h, int k, int l) const {
2035 switch (idx) {
2036 // 0-9: CCP4 hkl asu, 10-19: TNT hkl asu
2037 case 0: return l>0 || (l==0 && (h>0 || (h==0 && k>=0)));
2038 case 1: return k>=0 && (l>0 || (l==0 && h>=0));
2039 case 12: // orthorhombic-D
2040 case 2: return h>=0 && k>=0 && l>=0;
2041 case 3: return l>=0 && ((h>=0 && k>0) || (h==0 && k==0));
2042 case 14: // tetragonal-D, hexagonal-D
2043 case 4: return h>=k && k>=0 && l>=0;
2044 case 5: return (h>=0 && k>0) || (h==0 && k==0 && l>=0);
2045 case 16: // trigonal-D P312
2046 case 6: return h>=k && k>=0 && (k>0 || l>=0);
2047 case 17: // trigonal-D P321
2048 case 7: return h>=k && k>=0 && (h>k || l>=0);
2049 case 8: return h>=0 && ((l>=h && k>h) || (l==h && k==h));
2050 case 9: return k>=l && l>=h && h>=0;
2051 case 10: return k>0 || (k==0 && (h>0 || (h==0 && l>=0))); // triclinic
2052 case 11: return k>=0 && (h>0 || (h==0 && l>=0)); // monoclinic-B
2053 case 13: return l>=0 && ((k>=0 && h>0) || (h==0 && k==0)); // tetragonal-C, hexagonal-C
2054 case 15: return (k>=0 && h>0) || (h==0 && k==0 && l>=0); // trigonal-C
2055 case 18: return k>=0 && l>=0 && ((h>k && h>l) || (h==k && h>=l)); // cubic-T
2056 case 19: return h>=k && k>=l && l>=0; // cubic-O
2057 }
2058 unreachable();
2059 }
2060
2061 const char* condition_str() const {
2062 switch (idx) {
2063 case 0: return "l>0 or (l=0 and (h>0 or (h=0 and k>=0)))";
2064 case 1: return "k>=0 and (l>0 or (l=0 and h>=0))";
2065 case 12:
2066 case 2: return "h>=0 and k>=0 and l>=0";
2067 case 3: return "l>=0 and ((h>=0 and k>0) or (h=0 and k=0))";
2068 case 14:
2069 case 4: return "h>=k and k>=0 and l>=0";
2070 case 5: return "(h>=0 and k>0) or (h=0 and k=0 and l>=0)";
2071 case 16:
2072 case 6: return "h>=k and k>=0 and (k>0 or l>=0)";
2073 case 17:
2074 case 7: return "h>=k and k>=0 and (h>k or l>=0)";
2075 case 8: return "h>=0 and ((l>=h and k>h) or (l=h and k=h))";
2076 case 9: return "k>=l and l>=h and h>=0";
2077 case 10: return "k>0 or (k==0 and (h>0 or (h=0 and l>=0)))";
2078 case 11: return "k>=0 and (h>0 or (h=0 and l>=0))";
2079 case 13: return "l>=0 and ((k>=0 and h>0) or (h=0 and k==0))";
2080 case 15: return "(k>=0 and h>0) or (h=0 and k==0 and l>=0)";
2081 case 18: return "k>=0 and l>=0 and ((h>k and h>l) or (h=k and h>=l))";
2082 case 19: return "h>=k and k>=l and l>=0";
2083 }
2084 unreachable();
2085 }
2086
2089 std::pair<Op::Miller,int> to_asu(const Op::Miller& hkl, const GroupOps& gops) const {
2090 int isym = 0;
2091 for (const Op& op : gops.sym_ops) {
2092 ++isym;
2093 Op::Miller new_hkl = op.apply_to_hkl_without_division(hkl);
2094 if (is_in(new_hkl))
2095 return {Op::divide_hkl_by_DEN(new_hkl), isym};
2096 ++isym;
2098 if (is_in(negated_new_hkl))
2099 return {Op::divide_hkl_by_DEN(negated_new_hkl), isym};
2100 }
2101 fail("Oops, maybe inconsistent GroupOps?");
2102 }
2104 std::pair<Op::Miller,bool> to_asu_sign(const Op::Miller& hkl, const GroupOps& gops) const {
2105 std::pair<Op::Miller,bool> neg = {{0,0,0}, true};
2106 for (const Op& op : gops.sym_ops) {
2107 Op::Miller new_hkl = op.apply_to_hkl_without_division(hkl);
2108 if (is_in(new_hkl))
2109 return {Op::divide_hkl_by_DEN(new_hkl), true};
2111 if (is_in(negated_new_hkl))
2112 // don't return it yet, because for centric reflection we prefer (+)
2113 neg = {Op::divide_hkl_by_DEN(negated_new_hkl), false};
2114 }
2115 if (neg.second)
2116 fail("Oops, maybe inconsistent GroupOps?");
2117 return neg;
2118 }
2119};
2120
2121} // namespace gemmi
2122
2123namespace std {
2124template<> struct hash<gemmi::Op> {
2125 size_t operator()(const gemmi::Op& op) const {
2126 size_t h = 0;
2127 for (int i = 0; i != 3; ++i)
2128 for (int j = 0; j != 3; ++j)
2129 h = (h << 2) ^ (op.rot[i][j] + 1);
2130 for (int i = 0; i != 3; ++i)
2131 h = (h << 5) ^ op.tran[i];
2132 return h;
2133 }
2134};
2135} // namespace std
2136
2137#ifdef __clang__
2138# pragma clang diagnostic pop // ignored -Wmissing-braces
2139#endif
2140
2141#endif
Op parse_triplet(const std::string &s)
Definition symmetry.hpp:302
const char * get_basisop(int basisop_idx)
const SpaceGroup & get_spacegroup_p1()
CrystalSystem crystal_system(Laue laue)
Definition symmetry.hpp:971
GroupOps generators_from_hall(const char *hall)
Definition symmetry.hpp:856
const SpaceGroup & get_spacegroup_reference_setting(int number)
const SpaceGroup & get_spacegroup_by_name(const std::string &name)
int interpret_miller_character(char c, const std::string &s)
Definition symmetry.hpp:242
unsigned char point_group_index_and_category(int space_group_number)
Definition symmetry.hpp:988
const char * point_group_hm(PointGroup pg)
Definition symmetry.hpp:925
const SpaceGroup * find_spacegroup_by_name(std::string name, double alpha=0., double gamma=0.) noexcept
Op::Rot centred_to_primitive(char centring_type)
bool operator==(const Op &a, const Op &b)
Definition symmetry.hpp:188
GroupOps symops_from_hall(const char *hall)
Definition symmetry.hpp:900
PointGroup point_group(int space_group_number)
const SpaceGroup * find_spacegroup_by_number(int ccp4) noexcept
GroupOps split_centering_vectors(const std::vector< Op > &ops)
Definition symmetry.hpp:728
Vec3 operator*(double d, const Vec3 &v)
Definition math.hpp:104
Laue pointgroup_to_laue(PointGroup pg)
Definition symmetry.hpp:940
Op::Rot hall_rotation_z(int N)
Definition symmetry.hpp:749
Op & operator*=(Op &a, const Op &b)
Definition symmetry.hpp:194
const SpaceGroup & get_spacegroup_by_number(int ccp4)
bool is_enantiomorphic(int space_group_number)
Op::Tran nonzero_inversion_center(int space_group_number)
Inversion center of the Euclidean normalizer that is not at the origin of reference settings.
Op seitz_to_op(const std::array< std::array< double, 4 >, 4 > &t)
Definition symmetry.hpp:219
Op parse_hall_change_of_basis(const char *start, const char *end)
Definition symmetry.hpp:841
Op hall_matrix_symbol(const char *start, const char *end, int pos, int &prev)
Definition symmetry.hpp:779
std::string make_triplet_part(const std::array< int, 3 > &xyz, int w, char style='x')
Definition symmetry.hpp:366
std::vector< Op::Tran > centring_vectors(char centring_type)
Definition symmetry.hpp:415
const char * laue_class_str(Laue laue)
Definition symmetry.hpp:967
bool is_sohncke(int space_group_number)
void fail(const std::string &msg)
Definition fail.hpp:59
PointGroup laue_to_pointgroup(Laue laue)
Definition symmetry.hpp:958
Op::Tran hall_translation_from_symbol(char symbol)
Definition symmetry.hpp:763
const SpaceGroup * find_spacegroup_by_ops(const GroupOps &gops)
void unreachable()
Definition fail.hpp:80
bool is_symmorphic(int space_group_number)
bool operator!=(const Op &a, const Op &b)
Definition symmetry.hpp:191
CrystalSystem
Definition symmetry.hpp:908
std::array< int, 4 > parse_triplet_part(const std::string &s)
Definition symmetry.hpp:252
impl::Tables_< void > spacegroup_tables
const char * skip_blank(const char *p)
Definition atox.hpp:47
const char * crystal_system_str(CrystalSystem system)
Definition symmetry.hpp:912
Definition seqid.hpp:149
const GroupOps & gops
Definition symmetry.hpp:653
bool operator==(const Iter &other) const
Definition symmetry.hpp:664
bool operator!=(const Iter &other) const
Definition symmetry.hpp:667
Op * find_by_rotation(const Op::Rot &r)
Definition symmetry.hpp:476
const Op * find_by_rotation(const Op::Rot &r) const
Definition symmetry.hpp:483
bool has_same_centring(const GroupOps &other) const
Definition symmetry.hpp:599
void change_basis_backward(const Op &inv)
Definition symmetry.hpp:574
std::vector< Op > sym_ops
Definition symmetry.hpp:438
bool is_systematically_absent(const Op::Miller &hkl) const
Definition symmetry.hpp:515
char find_centering() const
Definition symmetry.hpp:461
void add_missing_elements()
Definition symmetry.hpp:674
int order() const
Definition symmetry.hpp:441
bool is_same_as(const GroupOps &other) const
Definition symmetry.hpp:592
void change_basis_forward(const Op &cob)
Definition symmetry.hpp:573
std::array< int, 3 > find_grid_factors() const
Definition symmetry.hpp:627
std::vector< Op::Tran > cen_ops
Definition symmetry.hpp:439
int epsilon_factor(const Op::Miller &hkl) const
Definition symmetry.hpp:507
void change_basis_impl(const Op &cob, const Op &inv)
Definition symmetry.hpp:531
bool is_centrosymmetric() const
Definition symmetry.hpp:487
void add_missing_elements_part2(const std::vector< Op > &gen, size_t max_size, bool ignore_bad_gen)
Definition symmetry.hpp:696
bool are_directions_symmetry_related(int u, int v) const
Definition symmetry.hpp:637
int epsilon_factor_without_centering(const Op::Miller &hkl) const
Definition symmetry.hpp:499
std::vector< Op > all_ops_sorted() const
Definition symmetry.hpp:576
Iter begin() const
Definition symmetry.hpp:670
bool add_inversion()
Definition symmetry.hpp:447
bool has_same_rotations(const GroupOps &other) const
Definition symmetry.hpp:612
GroupOps derive_symmorphic() const
Definition symmetry.hpp:645
static bool has_phase_shift(const Op::Tran &c, const Op::Miller &hkl)
Definition symmetry.hpp:511
bool is_reflection_centric(const Op::Miller &hkl) const
Definition symmetry.hpp:491
Op get_op(int n) const
Definition symmetry.hpp:586
Iter end() const
Definition symmetry.hpp:671
Op & wrap()
Definition symmetry.hpp:66
Op & translate(const Tran &a)
Definition symmetry.hpp:76
std::array< std::array< double, 4 >, 4 > float_seitz() const
Definition symmetry.hpp:168
Miller apply_to_hkl(const Miller &hkl) const
Definition symmetry.hpp:151
std::array< std::array< int, 4 >, 4 > int_seitz() const
Definition symmetry.hpp:160
std::array< std::array< int, 3 >, 3 > Rot
Definition symmetry.hpp:55
int rot_type() const
Definition symmetry.hpp:106
Rot negated_rot() const
Definition symmetry.hpp:86
std::string triplet(char style='x') const
Definition symmetry.hpp:405
static Miller divide_hkl_by_DEN(const Miller &hkl)
Definition symmetry.hpp:148
std::array< double, 3 > apply_to_xyz(const std::array< double, 3 > &xyz) const
Definition symmetry.hpp:131
std::array< int, 3 > Miller
Definition symmetry.hpp:140
Tran tran
Definition symmetry.hpp:59
static constexpr int DEN
Definition symmetry.hpp:54
std::array< int, 3 > Tran
Definition symmetry.hpp:56
Miller apply_to_hkl_without_division(const Miller &hkl) const
Definition symmetry.hpp:142
int det_rot() const
Definition symmetry.hpp:99
Rot transposed_rot() const
Definition symmetry.hpp:92
double phase_shift(const Miller &hkl) const
Definition symmetry.hpp:155
Op inverse() const
Definition symmetry.hpp:196
Op translated(const Tran &a) const
Definition symmetry.hpp:82
Op combine(const Op &b) const
Definition symmetry.hpp:116
static constexpr Op::Rot inversion_rot()
Definition symmetry.hpp:180
static constexpr Op identity()
Definition symmetry.hpp:177
Op add_centering(const Tran &a) const
Definition symmetry.hpp:84
bool operator<(const Op &rhs) const
Definition symmetry.hpp:183
bool is_in_reference_setting(int h, int k, int l) const
std::pair< Op::Miller, bool > to_asu_sign(const Op::Miller &hkl, const GroupOps &gops) const
Similar to to_asu(), but the second returned value is sign: true for + or centric.
bool is_in(const Op::Miller &hkl) const
std::pair< Op::Miller, int > to_asu(const Op::Miller &hkl, const GroupOps &gops) const
Returns hkl in asu and MTZ ISYM - 2*n-1 for reflections in the positive asu (I+ of a Friedel pair),...
const char * condition_str() const
ReciprocalAsu(const SpaceGroup *sg, bool tnt=false)
Op basisop() const
const char * laue_str() const
const char * basisop_str() const
bool is_symmorphic() const
GroupOps operations() const
std::string xhm() const
char monoclinic_unique_axis() const
returns 'a', 'b' or 'c' for monoclinic SG, '\0' otherwise
Laue laue_class() const
char ccp4_lattice_type() const
std::string short_name() const
const char * point_group_hm() const
Op change_of_hand_op() const
Returns change-of-hand operator. Compatible with similar sgtbx function.
const char * crystal_system_str() const
bool is_sohncke() const
char centring_type() const
std::string pdb_name() const
bool is_centrosymmetric() const
Op centred_to_primitive() const
bool is_enantiomorphic() const
bool is_reference_setting() const
CrystalSystem crystal_system() const
PointGroup point_group() const
size_t operator()(const gemmi::Op &op) const