1 #include "pointgroups.h"
6 double qpms_pg_quat_cmp_atol
= QPMS_QUAT_ATOL
;
8 #define PAIRCMP(a, b) {\
9 if ((a) < (b)) return -1;\
10 if ((a) > (b)) return 1;\
13 #define PAIRCMP_ATOL(a, b, atol) {\
14 if ((a) < (b) + (atol)) return -1;\
15 if ((a) + (atol) > (b)) return 1;\
18 int qpms_pg_irot3_approx_cmp(const qpms_irot3_t
*p1
,
19 const qpms_irot3_t
*p2
, double atol
) {
21 PAIRCMP(p1
->det
, p2
->det
);
22 const qpms_quat_t r1
= qpms_quat_standardise(p1
->rot
, atol
),
23 r2
= qpms_quat_standardise(p2
->rot
, atol
);
24 PAIRCMP_ATOL(creal(r1
.a
), creal(r2
.a
), atol
);
25 PAIRCMP_ATOL(cimag(r1
.a
), cimag(r2
.a
), atol
);
26 PAIRCMP_ATOL(creal(r1
.b
), creal(r2
.b
), atol
);
27 PAIRCMP_ATOL(cimag(r1
.b
), cimag(r2
.b
), atol
);
31 int qpms_pg_irot3_approx_cmp_v(const void *av
, const void *bv
) {
32 const qpms_irot3_t
*a
= av
, *b
= bv
;
33 return qpms_pg_irot3_approx_cmp(a
, b
, qpms_pg_quat_cmp_atol
);
37 /// Generates the canonical elements of a given 3D point group type.
38 qpms_irot3_t
*qpms_pg_canonical_elems(qpms_irot3_t
*target
,
39 qpms_pointgroup_class cls
, const qpms_gmi_t then
) {
41 const qpms_gmi_t order
= qpms_pg_order(cls
, then
);
42 QPMS_ENSURE(order
, "Cannot generate an infinite group!");
43 if (!target
) QPMS_CRASHING_MALLOC(target
, (1+order
) * sizeof(qpms_irot3_t
));
44 target
[0] = QPMS_IROT3_IDENTITY
;
45 qpms_gmi_t ngen
= qpms_pg_genset_size(cls
, then
);
46 qpms_irot3_t gens
[ngen
];
47 (void) qpms_pg_genset(cls
, then
, gens
);
49 // Let's try it with a binary search tree, as an exercise :)
51 // put the starting element (identity) to the tree
52 (void) tsearch((void *) target
, &root
, qpms_pg_irot3_approx_cmp_v
);
53 qpms_gmi_t n
= 1; // No. of generated elements.
55 // And let's do the DFS without recursion; the "stack size" here (=order) might be excessive, but whatever
56 qpms_gmi_t gistack
[order
], //< generator indices (related to gens[])
57 srcstack
[order
], //< pre-image indices (related to target[])
58 si
= 0; //< stack index
62 while (si
>= 0) { // DFS
63 if (gistack
[si
] < ngen
) { // are there generators left at this level? If so, try another elem
64 if (n
> order
) QPMS_WTF
; // TODO some error message
65 target
[n
] = qpms_irot3_mult(gens
[gistack
[si
]], target
[srcstack
[si
]]);
66 if (tfind((void *) &(target
[n
]), &root
, qpms_pg_irot3_approx_cmp_v
))
67 // elem found, try it with another gen in the next iteration
70 // elem not found, add it to the tree and proceed to next level
71 (void) tsearch( &(target
[n
]), &root
, qpms_pg_irot3_approx_cmp_v
);
77 } else { // no generators left at this level, get to the previous level
79 if (si
>= 0) ++gistack
[si
];
83 QPMS_ENSURE(n
== order
, "Point group generation failure "
84 "(assumed group order = %d, got %d; qpms_pg_quat_cmp_atol = %g)",
85 order
, n
, qpms_pg_quat_cmp_atol
);
87 while(root
) tdelete(*(qpms_irot3_t
**)root
, &root
, qpms_pg_irot3_approx_cmp_v
); // I hope this is the correct way.
92 qpms_irot3_t
*qpms_pg_elems(qpms_irot3_t
*target
, qpms_pointgroup_t g
) {
94 target
= qpms_pg_canonical_elems(target
, g
.c
, g
.n
);
95 qpms_gmi_t order
= qpms_pg_order(g
.c
, g
.n
);
96 const qpms_irot3_t o
= g
.orientation
, o_inv
= qpms_irot3_inv(o
);
97 for(qpms_gmi_t i
= 0 ; i
< order
; ++i
)
98 target
[i
] = qpms_irot3_mult(o_inv
, qpms_irot3_mult(target
[i
], o
));
102 _Bool
qpms_pg_is_subgroup(qpms_pointgroup_t small
, qpms_pointgroup_t big
) {
104 qpms_gmi_t order_big
= qpms_pg_order(big
.c
, big
.n
);
105 qpms_gmi_t order_small
= qpms_pg_order(small
.c
, small
.n
);
106 if (!order_big
|| !order_small
)
107 QPMS_NOT_IMPLEMENTED("Subgroup testing for infinite groups not implemented");
108 if (order_big
< order_small
) return false;
109 // TODO maybe some other fast-track negative decisions
111 qpms_irot3_t
*elems_small
= qpms_pg_elems(NULL
, small
);
112 qpms_irot3_t
*elems_big
= qpms_pg_elems(NULL
, small
);
113 qsort(elems_big
, order_big
, sizeof(qpms_irot3_t
), qpms_pg_irot3_approx_cmp_v
);
115 for(qpms_gmi_t smalli
= 0; smalli
< order_small
; ++smalli
) {
116 qpms_irot3_t
*match
= bsearch(&elems_small
[smalli
], elems_big
, order_big
,
117 sizeof(qpms_irot3_t
), qpms_pg_irot3_approx_cmp_v
);
118 if (!match
) return false;
124 /// Returns the order of a given 3D point group type.
125 /** For infinite groups returns 0. */
126 qpms_gmi_t
qpms_pg_order(qpms_pointgroup_class cls
, ///< Point group class.
127 qpms_gmi_t n
///< Number of rotations around main axis (only for finite axial groups).
129 if (qpms_pg_is_finite_axial(cls
))
130 QPMS_ENSURE(n
> 0, "n must be at least 1 for finite axial groups");
144 // Remaining polyhedral groups
158 // Continuous axial groups
165 // Remaining continuous groups
168 return 0; // 0 is infinity :-)
175 /// Returns the number of canonical generators of a given 3D point group type.
176 /** TODO what does it do for infinite (Lie) groups? */
177 qpms_gmi_t
qpms_pg_genset_size(qpms_pointgroup_class cls
, ///< Point group class.
178 qpms_gmi_t n
///< Number of rotations around main axis (only for axial groups).
180 if (qpms_pg_is_finite_axial(cls
)) {
181 QPMS_ENSURE(n
> 0, "n must be at least 1 for finite axial groups");
182 // n = 1 needs special care:
185 case QPMS_PGS_CN
: return 0; // triv.
186 case QPMS_PGS_S2N
: return 1; // Z_2
187 case QPMS_PGS_CNH
: return 1; // Dih_1
188 case QPMS_PGS_CNV
: return 1; // Dih_1
189 case QPMS_PGS_DN
: return 1; // Dih_1
190 case QPMS_PGS_DND
: return 2; // Dih_2
191 case QPMS_PGS_DNH
: return 2; // Dih_1 x Dih_1
198 case QPMS_PGS_CN
: return 1; // Z_n
199 case QPMS_PGS_S2N
: return 1; // Z_{2n}
200 case QPMS_PGS_CNH
: return 2; // Z_n x Dih_1
201 case QPMS_PGS_CNV
: return 2; // Dih_n
202 case QPMS_PGS_DN
: return 2; // Dih_n
203 case QPMS_PGS_DND
: return 2; // Dih_2n
204 case QPMS_PGS_DNH
: return 3; // Dih_n x Dih_1
206 // Remaining polyhedral groups
207 case QPMS_PGS_T
: // return ???; // A_4
208 case QPMS_PGS_TD
: // return 2; // S_4
209 case QPMS_PGS_TH
: // A_4 x Z_2
210 case QPMS_PGS_O
: // S_4
211 case QPMS_PGS_OH
: // return 3; // S_4 x Z_2
212 case QPMS_PGS_I
: // A_5
213 case QPMS_PGS_IH
: // A_5 x Z_2
215 // Continuous axial groups
222 // Remaining continuous groups
225 QPMS_NOT_IMPLEMENTED("Not yet implemented for this point group class.");
231 /// Fills an array of canonical generators of a given 3D point group type.
232 /** TODO what does it do for infinite (Lie) groups? */
233 qpms_gmi_t
qpms_pg_genset(qpms_pointgroup_class cls
, ///< Point group class.
234 qpms_gmi_t n
, ///< Number of rotations around main axis (only for axial groups).
235 qpms_irot3_t gen
[] ///< Target generator array
237 if (qpms_pg_is_finite_axial(cls
)) {
238 QPMS_ENSURE(n
> 0, "n must be at least 1 for finite axial groups");
239 // n = 1 needs special care:
245 gen
[0] = QPMS_IROT3_INVERSION
;
248 gen
[0] = QPMS_IROT3_ZFLIP
;
251 gen
[0] = QPMS_IROT3_XFLIP
;
254 gen
[0] = QPMS_IROT3_XROT_PI
; // CHECKME
257 gen
[0] = QPMS_IROT3_INVERSION
;
258 gen
[1] = QPMS_IROT3_XFLIP
;
260 case QPMS_PGS_DNH
: // CHECKME
261 gen
[0] = QPMS_IROT3_ZFLIP
;
262 gen
[1] = QPMS_IROT3_XFLIP
;
263 return 2; // Dih_1 x Dih_1
271 gen
[0] = qpms_irot3_zrot_Nk(n
, 1);
274 gen
[0].rot
= qpms_quat_zrot_Nk(2*n
, 1);
278 gen
[0] = qpms_irot3_zrot_Nk(n
, 1);
279 gen
[1] = QPMS_IROT3_ZFLIP
;
280 return 2; // Z_n x Dih_1
282 gen
[0] = qpms_irot3_zrot_Nk(n
, 1);
283 gen
[1] = QPMS_IROT3_XFLIP
;
286 gen
[0] = qpms_irot3_zrot_Nk(n
, 1);
287 gen
[1] = QPMS_IROT3_XROT_PI
;
290 gen
[0].rot
= qpms_quat_zrot_Nk(2*n
, 1);
292 gen
[1] = QPMS_IROT3_XFLIP
;
295 gen
[0] = qpms_irot3_zrot_Nk(n
, 1);
296 gen
[1] = QPMS_IROT3_ZFLIP
;
297 gen
[2] = QPMS_IROT3_XFLIP
;
298 return 3; // Dih_n x Dih_1
300 // Remaining polyhedral groups
301 case QPMS_PGS_T
: // return ???; // A_4
302 case QPMS_PGS_TD
: // return 2; // S_4
303 case QPMS_PGS_TH
: // A_4 x Z_2
304 case QPMS_PGS_O
: // S_4
305 case QPMS_PGS_OH
: // return 3; // S_4 x Z_2
306 case QPMS_PGS_I
: // A_5
307 case QPMS_PGS_IH
: // A_5 x Z_2
309 // Continuous axial groups
316 // Remaining continuous groups
319 QPMS_NOT_IMPLEMENTED("Not yet implemented for this point group class.");