2 * \brief Lattice point generators and lattice vector analysis / transformation.
14 #define M_SQRT3 1.7320508075688772935274463415058724
17 #define M_SQRT3_2 (M_SQRT3/2)
20 #define M_1_SQRT3 0.57735026918962576450914878050195746
28 * The current content of this part (and the implementation) is extremely ugly.
29 * When I have some time, I have to rewrite this in the style of lattices2d.py
33 typedef enum LatticeDimensionality
{
43 // special coordinate arrangements (indicating possible optimisations)
46 LAT_1D_IN_3D_ZONLY
= 97, // LAT1D | SPACE3D | 64
47 LAT_2D_IN_3D_XYONLY
= 162 // LAT2D | SPACE3D | 128
48 } LatticeDimensionality
;
50 inline static bool LatticeDimensionality_checkflags(
51 LatticeDimensionality a
, LatticeDimensionality flags_a_has_to_contain
) {
52 return ((a
& flags_a_has_to_contain
) == flags_a_has_to_contain
);
55 // fuck, I already had had suitable type
57 typedef cart2_t point2d
;
59 static inline point2d
point2d_fromxy(const double x
, const double y
) {
66 /// Lattice basis reduction.
67 /** This is currenty a bit naïve implementation of
68 * Lenstra-Lenstra-Lovász algorithm.
70 * The reduction happens in-place, i.e. the basis vectors in \a b are
71 * replaced with the reduced basis.
73 int qpms_reduce_lattice_basis(double *b
, ///< Array of dimension [bsize][ndim].
74 const size_t bsize
, ///< Number of the basis vectors (dimensionality of the lattice).
75 const size_t ndim
, ///< Dimension of the space into which the lattice is embedded.
76 /// Lovász condition parameter \f$ \delta \f$.
77 /** Polynomial time complexity guaranteed for \f$\delta \in (1/4,1)\f$.
82 /// Generic lattice point generator type.
84 * A bit of OOP-in-C brainfuck here.
86 * The basic principle of operation is following:
87 * Instead of a list (array) of points, an initialized PGen object
88 * is passed to a function that does something over a set of points.
89 * Each time PGen-type object is "called" (more specifically, one of
90 * the "methods" specified in the PGenClassInfo structure in @ref c,
91 * it returns PGenReturnData
92 * which contains a point in given coordinates (depending on the generator
93 * class) and some metadata.
95 * After the last generated point, the generator frees all internal memory
96 * and returns PGenSphReturnData with PGEN_NOTDONE flag unset (the rest
97 * shall be considered invalid data).
98 * The caller can also decide not to use the rest and end getting the points
99 * even when the PGEN_NOTDONE was set in the last returned data.
100 * In such case, the caller shall call PGen_destroy() manually.
105 * The standard wrapper "methods" to generate a single point in a given
106 * coordinate system are
109 * * PGen_next_cart2(),
110 * * PGen_next_cart3(),
114 * Memory management policy
115 * ------------------------
117 * The basic PGen structure shall be allocated on stack (it's only two pointers),
118 * everything internal goes on heap.
120 typedef struct PGen
{
121 /// Pointer to the "class" metadata defining the behaviour of the generator.
122 const struct PGenClassInfo
* /*const*/ c
;
123 /// Pointer to internal state data; shall be NULL if invalid (destroyed);
127 typedef enum PGenPointFlags
{
128 /** The most important flag: when this is not set, the
129 * interation ended – other data returned should be
130 * considered nonsense and at this point, the generator
131 * should have de-allocated all internal memory.
134 /** Set if the r-coordinate is not different than in the
135 * previous generated point (so radial parts of the
136 * calculation have to be redone).
140 /** Set if the r-coordinate has not changed between the
141 * first and the last point generated in the current
143 * Only for the bulk generator methods.
147 PGEN_AT_Z
= 4, ///< Set if the point(s) lie(s) at the z-axis (theta is either 0 or M_PI).
148 PGEN_AT_XY
= 8, ///< Set if the point(s) lie(s) in the xy-plane (theta is M_PI2).
149 PGEN_METHOD_UNAVAILABLE
= 2048, ///< Set if no suitable method exists (no point generated).
150 PGEN_DONE
= 0, ///< Convenience identifier, not an actual flag.
151 PGEN_COORDS_CART1
= QPMS_COORDS_CART1
,
152 PGEN_COORDS_CART2
= QPMS_COORDS_CART2
,
153 PGEN_COORDS_CART3
= QPMS_COORDS_CART3
,
154 PGEN_COORDS_POL
= QPMS_COORDS_POL
,
155 PGEN_COORDS_SPH
= QPMS_COORDS_SPH
,
156 PGEN_COORDS_BITRANGE
= QPMS_COORDS_BITRANGE
,
160 /// Metadata generated by the fetch*() methods from PGenClassInfo
161 typedef struct PGenReturnDataBulk
{
162 /// Flags describing the returned data.
163 PGenPointFlags flags
;
164 size_t generated
; ///< Number of points really generated
165 } PGenReturnDataBulk
;
167 /// Generic PGen return type that might contain point represented in any of the supported coordinate systems.
168 typedef struct PGenReturnData
{
169 PGenPointFlags flags
; ///< Metadata, must contain valid coordinate system defining flags.
170 anycoord_point_t point
; ///< Generated point in a coordinate system defined by flags.
173 /// PGen single-point return data type (1D).
174 typedef struct PGenZReturnData
{
175 PGenPointFlags flags
; ///< Medatata.
176 double point_z
; ///< Generated point on a real axis.
179 /// PGen single-point return data type (2D, polar coordinates).
180 typedef struct PGenPolReturnData
{
181 PGenPointFlags flags
; ///< Metadata.
182 pol_t point_pol
; ///< Generated point in polar coordinates.
185 /// PGen single-point return data type (3D, spherical coordinates).
186 typedef struct PGenSphReturnData
{
187 PGenPointFlags flags
; ///< Metadata.
188 sph_t point_sph
; ///< Generated point in spherical coordinates.
191 /// PGen single-point return data type (2D, cartesian coordinates).
192 typedef struct PGenCart2ReturnData
{
193 PGenPointFlags flags
; ///< Metadata.
194 cart2_t point_cart2
; ///< Generated point in cartesian coordinates.
195 } PGenCart2ReturnData
;
197 /// PGen single-point return data type (3D, cartesian coordinates).
198 typedef struct PGenCart3ReturnData
{
199 PGenPointFlags flags
; ///< Metadata.
200 cart3_t point_cart3
; ///< Generated point in cartesian coordinates.
201 } PGenCart3ReturnData
;
203 // convenience constants for use in the extractor implementations
204 static const PGenZReturnData PGenZDoneVal
= {PGEN_DONE
, 0};
205 static const PGenPolReturnData PGenPolDoneVal
= {PGEN_DONE
, {0,0}};
206 static const PGenSphReturnData PGenSphDoneVal
= {PGEN_DONE
, {0,0,0}};
207 static const PGenCart2ReturnData PGenCart2DoneVal
= {PGEN_DONE
, {0,0}};
208 static const PGenCart3ReturnData PGenCart3DoneVal
= {PGEN_DONE
, {0,0,0}};
211 /// PGen class metadata.
213 * This structure determines the behaviour of the PGen
214 * instance pointing to it.
216 * For generating a single point, use the next() method.
217 * For generating up to N points in a single call, use the
220 * It is strongly recommended that at least the native-coordinate
221 * fetch method and the native-coordinate next method are implemented.
223 * Usually, each generator uses internally one "native" coordinate
224 * system (in lattice generators, this will typically be nD
225 * cartesian coordinates) in which the next() method gives its result.
227 * One does not have to explicitly implement every single method.
229 * TODO doc about the default transformations etc.
231 typedef struct PGenClassInfo
{ // static PGenSph info
232 char * const name
; // mainly for debugging purposes
233 int dimensionality
; // lower-dimensional can be converted to higher-D, not vice versa; bit redundant with the following, whatever.
234 /// Info about the generator native coordinate system.
235 PGenPointFlags native_point_flags
;
236 /// Generate a single point in the native coordinates.
237 PGenReturnData (*next
)(struct PGen
*);
238 /// Generate a single 1D point.
239 PGenZReturnData (*next_z
)(struct PGen
*);
240 /// Generate a single 2D point in polar coordinates.
241 PGenPolReturnData (*next_pol
)(struct PGen
*);
242 /// Generate a single 3D point in spherical coordinates.
243 PGenSphReturnData (*next_sph
)(struct PGen
*);
244 /// Generate a single 2D point in cartesian coordinates.
245 PGenCart2ReturnData (*next_cart2
)(struct PGen
*);
246 /// Generate a single 3D point in cartesian coordinates.
247 PGenCart3ReturnData (*next_cart3
)(struct PGen
*);
248 /// Generate up to \a n points in the native coordinates.
249 PGenReturnDataBulk (*fetch
)(struct PGen
*, size_t, anycoord_point_t
*);
250 /// Generate up to \a n 1D points.
251 PGenReturnDataBulk (*fetch_z
)(struct PGen
*, size_t, double *);
252 /// Generate up to \a n 2D points in polar coordinates.
253 PGenReturnDataBulk (*fetch_pol
)(struct PGen
*, size_t, pol_t
*);
254 /// Generate up to \a n 3D points in spherical coordinates.
255 PGenReturnDataBulk (*fetch_sph
)(struct PGen
*, size_t, sph_t
*);
256 /// Generate up to \a n 2D points in cartesian coordinates.
257 PGenReturnDataBulk (*fetch_cart2
)(struct PGen
*, size_t, cart2_t
*);
258 /// Generate up to \a n 3D points in cartesian coordinates.
259 PGenReturnDataBulk (*fetch_cart3
)(struct PGen
*, size_t, cart3_t
*);
261 /** To be called by next() at iteration end, or by the caller
262 * if ending the generation prematurely
264 void (*destructor
)(struct PGen
*);
267 /// Generate a point with any of the next-methods.
268 static inline PGenReturnData
PGen_next_nf(struct PGen
*g
) {
270 return g
->c
->next(g
);
274 PGenZReturnData res
= g
->c
->next_z(g
);
276 r
.point
.z
= res
.point_z
;
277 } else if (g
->c
->next_pol
) {
278 PGenPolReturnData res
= g
->c
->next_pol(g
);
280 r
.point
.pol
= res
.point_pol
;
281 } else if (g
->c
->next_cart2
) {
282 PGenCart2ReturnData res
= g
->c
->next_cart2(g
);
284 r
.point
.cart2
= res
.point_cart2
;
285 } else if (g
->c
->next_sph
) {
286 PGenSphReturnData res
= g
->c
->next_sph(g
);
288 r
.point
.sph
= res
.point_sph
;
289 } else if (g
->c
->next_cart3
) {
290 PGenCart3ReturnData res
= g
->c
->next_cart3(g
);
292 r
.point
.cart3
= res
.point_cart3
;
294 r
.flags
= PGEN_METHOD_UNAVAILABLE
;
299 /// Generate multiple points with PGen in any coordinate system.
301 static inline PGenReturnDataBulk
PGen_fetch_any(struct PGen
*g
, size_t nmemb
,
302 anycoord_point_t
*target
) {
304 return g
->c
->fetch(g
, nmemb
, target
);
305 else if (g
->c
->fetch_cart3
) {
306 cart3_t
*t2
= (cart3_t
*) ((char *) target
307 + nmemb
* (sizeof(anycoord_point_t
)-sizeof(cart3_t
)));
308 PGenReturnDataBulk res
= g
->c
->fetch_cart3(g
, nmemb
, t2
);
309 for (size_t i
= 0; i
< nmemb
; ++i
)
310 target
[i
].cart3
= t2
[i
];
312 } else if (g
->c
->fetch_sph
) {
313 sph_t
*t2
= (sph_t
*) ((char *) target
314 + nmemb
* (sizeof(anycoord_point_t
)-sizeof(sph_t
)));
315 PGenReturnDataBulk res
= g
->c
->fetch_sph(g
, nmemb
, t2
);
316 for (size_t i
= 0; i
< nmemb
; ++i
)
317 target
[i
].sph
= t2
[i
];
319 } else if (g
->c
->fetch_cart2
) {
320 cart2_t
*t2
= (cart2_t
*) ((char *) target
321 + nmemb
* (sizeof(anycoord_point_t
)-sizeof(cart2_t
)));
322 PGenReturnDataBulk res
= g
->c
->fetch_cart2(g
, nmemb
, t2
);
323 for (size_t i
= 0; i
< nmemb
; ++i
)
324 target
[i
].cart2
= t2
[i
];
326 } else if (g
->c
->fetch_pol
) {
327 pol_t
*t2
= (pol_t
*) ((char *) target
328 + nmemb
* (sizeof(anycoord_point_t
)-sizeof(pol_t
)));
329 PGenReturnDataBulk res
= g
->c
->fetch_pol(g
, nmemb
, t2
);
330 for (size_t i
= 0; i
< nmemb
; ++i
)
331 target
[i
].pol
= t2
[i
];
333 } else if (g
->c
->fetch_z
) {
334 double *t2
= (double*) ((char *) target
335 + nmemb
* (sizeof(anycoord_point_t
)-sizeof(double)));
336 PGenReturnDataBulk res
= g
->c
->fetch_z(g
, nmemb
, t2
);
337 for (size_t i
= 0; i
< nmemb
; ++i
)
341 // This is ridiculously inefficient
342 PGenReturnDataBulk res
= {PGEN_NOTDONE
, 0};
343 for (res
.generated
= 0; res
.generated
< nmemb
;
345 PGenReturnData res1
= PGen_next_nf(g
);
346 QPMS_ENSURE(!(res1
.flags
& PGEN_METHOD_UNAVAILABLE
),
347 "No method found to generate points. The PGenClassInfo"
348 " %s is apparently broken.", g
->c
->name
);
349 if (res1
.flags
& PGEN_NOTDONE
) {
350 target
[res
.generated
] = res1
.point
;
351 // The coordinate system generated by next() must be consistent:
352 assert(!res
.generated
|| ((res1
.flags
& PGEN_COORDS_BITRANGE
) == (res
.flags
& PGEN_COORDS_BITRANGE
)));
353 res
.flags
|= res1
.flags
& PGEN_COORDS_BITRANGE
;
355 res
.flags
&= ~PGEN_NOTDONE
;
359 // Do not guarantee anything for; low priority TODO
360 res
.flags
&= ~(PGEN_OLD_R
& PGEN_SINGLE_R
);
365 /// Generate a point with any of the next-methods or fetch-methods.
366 static inline PGenReturnData
PGen_next(struct PGen
*g
) {
367 PGenReturnData res
= PGen_next_nf(g
);
368 if (!(res
.flags
& PGEN_METHOD_UNAVAILABLE
))
370 else { // Slow if implementation is stupid, but short!
371 PGenReturnDataBulk resb
= PGen_fetch_any(g
, 1, &res
.point
);
373 // the | PGEN_NOTDONE may not be needed, but my brain melted
374 res
.flags
= resb
.flags
| PGEN_NOTDONE
;
376 res
.flags
= PGEN_DONE
;
381 /// Generate multiple points in spherical coordinates.
382 static inline PGenReturnDataBulk
PGen_fetch_sph(struct PGen
*g
,
383 size_t nmemb
, sph_t
*target
) {
385 return g
->c
->fetch_sph(g
, nmemb
, target
);
387 anycoord_point_t
*tmp
;
388 QPMS_CRASHING_MALLOC(tmp
, sizeof(anycoord_point_t
) * nmemb
);
389 PGenReturnDataBulk res
= PGen_fetch_any(g
, nmemb
, tmp
);
390 anycoord_arr2something(target
, QPMS_COORDS_SPH
,
391 tmp
, res
.flags
, res
.generated
);
393 res
.flags
= (res
.flags
& ~QPMS_COORDS_BITRANGE
)
399 /// Generate multiple points in 3D cartesian coordinates.
400 static inline PGenReturnDataBulk
PGen_fetch_cart3(struct PGen
*g
,
401 size_t nmemb
, cart3_t
*target
) {
402 if (g
->c
->fetch_cart3
)
403 return g
->c
->fetch_cart3(g
, nmemb
, target
);
405 anycoord_point_t
*tmp
;
406 QPMS_CRASHING_MALLOC(tmp
, sizeof(anycoord_point_t
) * nmemb
);
407 PGenReturnDataBulk res
= PGen_fetch_any(g
, nmemb
, tmp
);
408 anycoord_arr2something(target
, QPMS_COORDS_CART3
,
409 tmp
, res
.flags
, res
.generated
);
411 res
.flags
= (res
.flags
& ~QPMS_COORDS_BITRANGE
)
417 /// Generate multiple points in polar coordinates.
418 static inline PGenReturnDataBulk
PGen_fetch_pol(struct PGen
*g
,
419 size_t nmemb
, pol_t
*target
) {
421 return g
->c
->fetch_pol(g
, nmemb
, target
);
423 anycoord_point_t
*tmp
;
424 QPMS_CRASHING_MALLOC(tmp
, sizeof(anycoord_point_t
) * nmemb
);
425 PGenReturnDataBulk res
= PGen_fetch_any(g
, nmemb
, tmp
);
426 anycoord_arr2something(target
, QPMS_COORDS_POL
,
427 tmp
, res
.flags
, res
.generated
);
429 res
.flags
= (res
.flags
& ~QPMS_COORDS_BITRANGE
)
435 /// Generate multiple points in 2D cartesian coordinates.
436 static inline PGenReturnDataBulk
PGen_fetch_cart2(struct PGen
*g
,
437 size_t nmemb
, cart2_t
*target
) {
438 if (g
->c
->fetch_cart2
)
439 return g
->c
->fetch_cart2(g
, nmemb
, target
);
441 anycoord_point_t
*tmp
;
442 QPMS_CRASHING_MALLOC(tmp
, sizeof(anycoord_point_t
) * nmemb
);
443 PGenReturnDataBulk res
= PGen_fetch_any(g
, nmemb
, tmp
);
444 anycoord_arr2something(target
, QPMS_COORDS_CART2
,
445 tmp
, res
.flags
, res
.generated
);
447 res
.flags
= (res
.flags
& ~QPMS_COORDS_BITRANGE
)
453 /// Generate multiple points in 1D cartesian coordinates.
454 static inline PGenReturnDataBulk
PGen_fetch_z(struct PGen
*g
,
455 size_t nmemb
, double *target
) {
457 return g
->c
->fetch_z(g
, nmemb
, target
);
459 anycoord_point_t
*tmp
;
460 QPMS_CRASHING_MALLOC(tmp
, sizeof(anycoord_point_t
) * nmemb
);
461 PGenReturnDataBulk res
= PGen_fetch_any(g
, nmemb
, tmp
);
462 anycoord_arr2something(target
, QPMS_COORDS_CART1
,
463 tmp
, res
.flags
, res
.generated
);
465 res
.flags
= (res
.flags
& ~QPMS_COORDS_BITRANGE
)
471 /// Deallocate and invalidate a PGen point generator.
472 static inline void PGen_destroy(PGen
*g
) {
474 assert(g
->stateData
== NULL
); // this should be done by the destructor
477 /// Generate a point in a 1D real space.
478 static inline PGenZReturnData
PGen_next_z(PGen
*g
) {
480 return g
->c
->next_z(g
);
481 else { // Super-slow generic fallback.
482 PGenReturnData res
= PGen_next(g
);
483 if (res
.flags
& PGEN_NOTDONE
) {
485 r
.point_z
= anycoord2cart1(res
.point
, res
.flags
);
486 r
.flags
= (res
.flags
& ~QPMS_COORDS_BITRANGE
)
494 /// Generate a point in a 3D real space (spherical coordinates).
495 static inline PGenSphReturnData
PGen_next_sph(PGen
*g
) {
497 return g
->c
->next_sph(g
);
498 else { // Super-slow generic fallback.
499 PGenReturnData res
= PGen_next(g
);
500 if (res
.flags
& PGEN_NOTDONE
) {
502 r
.point_sph
= anycoord2sph(res
.point
, res
.flags
);
503 r
.flags
= (res
.flags
& ~QPMS_COORDS_BITRANGE
)
507 return PGenSphDoneVal
;
511 /// Generate a point in a 2D real space (polar coordinates).
512 static inline PGenPolReturnData
PGen_next_pol(PGen
*g
) {
514 return g
->c
->next_pol(g
);
515 else { // Super-slow generic fallback.
516 PGenReturnData res
= PGen_next(g
);
517 if (res
.flags
& PGEN_NOTDONE
) {
519 r
.point_pol
= anycoord2pol(res
.point
, res
.flags
);
520 r
.flags
= (res
.flags
& ~QPMS_COORDS_BITRANGE
)
524 return PGenPolDoneVal
;
528 /// Generate a point in a 3D real space (cartesian coordinates).
529 static inline PGenCart3ReturnData
PGen_next_cart3(PGen
*g
) {
530 if (g
->c
->next_cart3
)
531 return g
->c
->next_cart3(g
);
532 else { // Super-slow generic fallback.
533 PGenReturnData res
= PGen_next(g
);
534 if (res
.flags
& PGEN_NOTDONE
) {
535 PGenCart3ReturnData r
;
536 r
.point_cart3
= anycoord2cart3(res
.point
, res
.flags
);
537 r
.flags
= (res
.flags
& ~QPMS_COORDS_BITRANGE
)
541 return PGenCart3DoneVal
;
545 /// Generate a point in a 2D real space (cartesian coordinates).
546 static inline PGenCart2ReturnData
PGen_next_cart2(PGen
*g
) {
547 if (g
->c
->next_cart2
)
548 return g
->c
->next_cart2(g
);
549 else { // Super-slow generic fallback.
550 PGenReturnData res
= PGen_next(g
);
551 if (res
.flags
& PGEN_NOTDONE
) {
552 PGenCart2ReturnData r
;
553 r
.point_cart2
= anycoord2cart2(res
.point
, res
.flags
);
554 r
.flags
= (res
.flags
& ~QPMS_COORDS_BITRANGE
)
558 return PGenCart2DoneVal
;
563 /// Generate up to \a n points.
564 static inline PGenReturnDataBulk
PGen_fetch(PGen
*g
, size_t n
, anycoord_point_t
*arr
){
566 return g
->c
->fetch(g
, n
, arr
);
571 static inline bool PGenSph_notDone(PGenSphReturnData data
) {
572 return data
.flags
& PGEN_NOTDONE
? true : false;
574 static inline bool PGenCart3_notDone(PGenCart3ReturnData data
) {
575 return data
.flags
& PGEN_NOTDONE
? true : false;
578 /// Standard PGen spherical coordinates -> 3d cartesian convertor.
579 static inline PGenCart3ReturnData
PGenReturnDataConv_sph_cart3(PGenSphReturnData sphdata
){
580 PGenCart3ReturnData c3data
;
581 c3data
.flags
= sphdata
.flags
;
582 c3data
.point_cart3
= sph2cart(sphdata
.point_sph
);
586 /// Standard PGen 3d cartesian -> spherical coordinates convertor.
587 static inline PGenSphReturnData
PGenReturnDataConv_cart3_sph(PGenCart3ReturnData c
){
590 s
.point_sph
= cart2sph(c
.point_cart3
);
595 * Some basic lattice generators implementing the abstract interface above (implemented in latticegens.c).
598 /// 2D point generator that simply iterates over an existing array of Point2d.
599 extern const PGenClassInfo PGen_FromPoint2DArray
; // TODO Do I even need this to be declared here?
600 /// PGen_FromPoint2DArray constructor.
601 PGen
PGen_FromPoints2DArray_new(const point2d
*points
, size_t len
);
603 /// 1D equidistant point generator.
604 extern const PGenClassInfo PGen_1D
;
605 typedef enum PGen_1D_incrementDirection
{
606 //PGEN_1D_POSITIVE_INC, // not implemented
607 //PGEN_1D_NEGATIVE_INC, // not implemented
608 PGEN_1D_INC_FROM_ORIGIN
,
609 PGEN_1D_INC_TOWARDS_ORIGIN
610 } PGen_1D_incrementDirection
;
611 /// PGen_1D point generator constructor.
612 PGen
PGen_1D_new_minMaxR(double period
, ///< Distance between points.
613 double offset
, ///< Lattice offset from zero.
614 double minR
, ///< Lower bound of |z| of the generated points.
615 bool inc_minR
, ///< Include also |z| == minR (if applicable).
616 double maxR
, ///< Upper bound of |z| of the generated points.
617 bool inc_maxR
, ///< Include also |z| == maxR if applicable.
618 PGen_1D_incrementDirection incdir
///< Order of generated points.
621 extern const PGenClassInfo PGen_xyWeb
;
622 PGen
PGen_xyWeb_new(cart2_t b1
, cart2_t b2
, double rtol
, cart2_t offset
,
623 double minR
, bool inc_minR
, double maxR
, bool inc_maxR
);
625 /// Returns a number larger or equal than the number of all the points generated by a PGen_xyWeb.
626 size_t PGen_xyWeb_sizecap(cart2_t b1
, cart2_t b2
, double rtol
, cart2_t offset
,
627 double minR
, bool inc_minR
, double maxR
, bool inc_maxR
);
630 extern const PGenClassInfo PGen_LatticeRadialHeap2D
;
631 extern const PGenClassInfo PGen_LatticeRadialHeap3D
;
632 PGen
PGen_LatticeRadialHeap2D_new(cart2_t b1
, cart2_t b2
, cart2_t offset
,
633 double minR
, bool inc_minR
, double maxR
, bool inc_maxR
);
634 PGen
PGen_LatticeRadialHeap3D_new(const cart3_t
*b1
, const cart3_t
*b2
, const cart3_t
*b3
,
635 const cart3_t
*offset
, double minR
, bool inc_minR
, double maxR
, bool inc_maxR
);
639 * THE NICE PART (adaptation of lattices2d.py)
640 * ===========================================
642 * all the functions are prefixed with l2d_
643 * convention for argument order: inputs, *outputs, add. params
646 #define BASIS_RTOL 1e-13
648 // Bravais lattice types
654 EQUILATERAL_TRIANGULAR
= 3,
655 RIGHT_ISOSCELES
=SQUARE
,
656 PARALLELOGRAMMIC
=OBLIQUE
,
657 CENTERED_RHOMBIC
=RECTANGULAR
,
658 RIGHT_TRIANGULAR
=RECTANGULAR
,
659 CENTERED_RECTANGULAR
=RHOMBIC
,
660 ISOSCELE_TRIANGULAR
=RHOMBIC
,
661 RIGHT_ISOSCELE_TRIANGULAR
=SQUARE
,
662 HEXAGONAL
=EQUILATERAL_TRIANGULAR
672 // Just for detecting the right angles (needed for generators).
683 * Lagrange-Gauss reduction of a 2D basis.
684 * The output shall satisfy |out1| <= |out2| <= |out2 - out1|
686 void l2d_reduceBasis(cart2_t in1
, cart2_t in2
, cart2_t
*out1
, cart2_t
*out2
);
688 // This one uses LLL reduction.
689 void l3d_reduceBasis(const cart3_t in
[3], cart3_t out
[3]);
692 * This gives the "ordered shortest triple" of base vectors (each pair from the triple
693 * is a base) and there may not be obtuse angle between o1, o2 and between o2, o3
695 void l2d_shortestBase3(cart2_t i1
, cart2_t i2
, cart2_t
*o1
, cart2_t
*o2
, cart2_t
*o3
);
699 * return value is 4 or 6.
701 int l2d_shortestBase46(cart2_t i1
, cart2_t i2
, cart2_t
*o1
, cart2_t
*o2
, cart2_t
*o3
, cart2_t
*o4
, cart2_t
*o5
, cart2_t
*o6
, double rtol
);
703 int l2d_shortestBase46_arr(cart2_t i1
, cart2_t i2
, cart2_t
*oarr
, double rtol
);
705 // Determines whether angle between inputs is obtuse
706 bool l2d_is_obtuse_r(cart2_t i1
, cart2_t i2
, double rtol
);
707 bool l2d_is_obtuse(cart2_t i1
, cart2_t i2
);
710 * Given two basis vectors, returns 2D Bravais lattice type.
712 LatticeType2
l2d_classifyLattice(cart2_t b1
, cart2_t b2
, double rtol
);
714 // Detects right angles.
715 LatticeFlags
l2d_detectRightAngles(cart2_t b1
, cart2_t b2
, double rtol
);
716 LatticeFlags
l3d_detectRightAngles(const cart3_t basis
[3], double rtol
);
718 // Other functions in lattices2d.py: TODO?
721 // generateLatticeDisk()
727 * Given basis vectors, returns the corners of the Wigner-Seits unit cell (W1, W2, -W1, W2)
728 * for rectangular and square lattice or (w1, w2, w3, -w1, -w2, -w3) otherwise.
730 int l2d_cellCornersWS(cart2_t i1
, cart2_t i2
, cart2_t
*o1
, cart2_t
*o2
, cart2_t
*o3
, cart2_t
*o4
, cart2_t
*o5
, cart2_t
*o6
, double rtol
);
732 int l2d_cellCornersWS_arr(cart2_t i1
, cart2_t i2
, cart2_t
*oarr
, double rtol
);
734 // Reciprocal bases; returns 0 on success, possibly a non-zero if b1 and b2 are parallel
735 int l2d_reciprocalBasis1(cart2_t b1
, cart2_t b2
, cart2_t
*rb1
, cart2_t
*rb2
);
736 int l2d_reciprocalBasis2pi(cart2_t b1
, cart2_t b2
, cart2_t
*rb1
, cart2_t
*rb2
);
737 // 3D reciprocal bases; returns (direct) unit cell volume with possible sign. Assumes direct lattice basis already reduced.
738 double l3d_reciprocalBasis1(const cart3_t direct_basis
[3], cart3_t reciprocal_basis
[3]);
739 double l3d_reciprocalBasis2pi(const cart3_t direct_basis
[3], cart3_t reciprocal_basis
[3]);
741 double l2d_unitcell_area(cart2_t b1
, cart2_t b2
);
743 // returns the radius of inscribed circle of a hexagon (or rectangle/square if applicable) created by the shortest base triple
744 double l2d_hexWebInCircleRadius(cart2_t b1
, cart2_t b2
);
747 * THE MORE OR LESS OK PART
748 * ========================
752 * General set of points ordered by the r-coordinate.
753 * Typically, this will include all lattice inside a certain circle.
754 * This structure is internally used by the "lattice generators" below.
755 * It does not have its memory management of its own, as it is handled
756 * by the "generators". For everything except the generators,
757 * this structure shall be read-only.
760 size_t nrs
; // number of different radii
761 double *rs
; // the radii; of length nrs (largest contained radius == rs[nrs-1])
763 ptrdiff_t *r_offsets
; // of length nrs+1 (using relative offsets due to possible realloc's)
764 // the jth point of i-th radius is base[r_offsets[i]+j] or using the inline below..
765 /* // redundand (therefore removed) members
766 * point2d *points; // redundant as it is the same as points_at_r[0]
767 * size_t npoints; // redundant as it is the same as points_at_r[nrs]-points_at_r[0]
769 } points2d_rordered_t
;
772 // sorts arbitrary points and creates points2d_rordered_t
773 points2d_rordered_t
*points2d_rordered_frompoints(const point2d
*orig_base
,
774 size_t nmemb
, double rtol
, double atol
);
776 // returns a copy but shifted by a constant (actually in a stupid way, but whatever)
777 points2d_rordered_t
*points2d_rordered_shift(const points2d_rordered_t
*orig
,
778 point2d shift
, double rtol
, double atol
);
780 // returns a copy but scaled by a factor
781 points2d_rordered_t
*points2d_rordered_scale(const points2d_rordered_t
*orig
,
785 /* The destructor: use only for results of
786 * - points2D_rordered_frompoints,
787 * - points2d_rordered_shift,
788 * - points2d_rordered_scale.
790 void points2d_rordered_free(points2d_rordered_t
*);
792 static inline point2d
points2d_rordered_get_point(const points2d_rordered_t
*ps
, int r_order
, int i
) {
794 assert(r_order
< ps
->nrs
);
795 assert(i
< (ps
->r_offsets
[r_order
+1] - ps
->r_offsets
[r_order
]));
796 return ps
->base
[ps
->r_offsets
[r_order
] + i
];
799 static inline double points2d_rordered_get_r(const points2d_rordered_t
*ps
, int r_order
) {
800 assert(r_order
< ps
->nrs
);
801 return ps
->rs
[r_order
];
805 ptrdiff_t points2d_rordered_locate_r(const points2d_rordered_t
*, double r
);
807 // returns a "view" (does not copy any of the arrays)
808 // -- DO NOT FREE orig BEFORE THE END OF SCOPE OF THE RESULT
809 points2d_rordered_t
points2d_rordered_annulus(const points2d_rordered_t
*orig
, double minr
, bool minr_inc
,
810 double maxr
, bool maxr_inc
);
814 /// Gives the frequency of \a n-th empty lattice mode at a given wave vector \a k.
815 double qpms_emptylattice2_mode_nth(
816 cart2_t b1_rec
, ///< First reciprocal lattice base vector
817 cart2_t b2_rec
, ///< Second reciprocal lattice base vector
818 double rtol
, ///< Relative tolerance to detect right angles
819 cart2_t k
, ///< The wave vector
820 double wave_speed
, ///< Wave speed in a given medium (i.e. vacuum speed / refractive index).
821 size_t N
///< Index of the mode (note that degenerate modes are counted multiple times).
824 /// Gives the first `maxindex` frequencies of empty lattice modes at a given wave vector \a k.
825 void qpms_emptylattice2_modes_maxindex(
826 double target_freqs
[], ///< Target array of size maxindex.
827 cart2_t b1_rec
, ///< First reciprocal lattice base vector
828 cart2_t b2_rec
, ///< Second reciprocal lattice base vector
829 double rtol
, ///< Relative tolerance to detect right angles
830 cart2_t k
, ///< The wave vector
831 double wave_speed
, ///< Wave speed in a given medium (i.e. vacuum speed / refractive index).
832 size_t maxindex
///< Number of the frequencies generated.
836 /// Gives the frequencies of empty lattice modes at a given wave vector \a k up to \a maxfreq and one more.
838 * The frequencies are saved to a newly allocated array *target_freqs (to be deallocated
839 * using free() by the caller).
841 * \returns Number of found mode frequencies lower or equal than \a maxfreq plus one.
843 size_t qpms_emptylattice2_modes_maxfreq(
844 double **target_freqs
,
845 cart2_t b1_rec
, ///< First reciprocal lattice base vector
846 cart2_t b2_rec
, ///< Second reciprocal lattice base vector
847 double rtol
, ///< Relative tolerance to detect right angles
848 cart2_t k
, ///< The wave vector
849 double wave_speed
, ///< Wave speed in a given medium (i.e. vacuum speed / refractive index).
850 double maxfreq
///< The maximum frequency.
853 /// Gives the frequencies of two empty lattice modes nearest to \a omega at a given wave vector \a k.
854 void qpms_emptylattice2_modes_nearest(
855 double target
[2], ///< Target array with lower ([0]) and upper ([1]) frequency.
856 cart2_t b1_rec
, ///< First reciprocal lattice base vector
857 cart2_t b2_rec
, ///< Second reciprocal lattice base vector
858 double rtol
, ///< Relative tolerance to detect right angles
859 cart2_t k
, ///< The wave vector
860 double wave_speed
, ///< Wave speed in a given medium (i.e. vacuum speed / refractive index).
861 double omega
///< The frequency around which the frequencies are searched.
872 * EQUILATERAL TRIANGULAR LATTICE
876 TRIANGULAR_VERTICAL
, // there is a lattice base vector parallel to the y-axis
877 TRIANGULAR_HORIZONTAL
// there is a lattice base vector parallel to the x-axis
878 } TriangularLatticeOrientation
;
880 static inline TriangularLatticeOrientation
reverseTriangularLatticeOrientation(TriangularLatticeOrientation o
){
882 case TRIANGULAR_VERTICAL
:
883 return TRIANGULAR_HORIZONTAL
;
885 case TRIANGULAR_HORIZONTAL
:
886 return TRIANGULAR_VERTICAL
;
894 // implementation data structures; not needed in the header file
895 typedef struct triangular_lattice_gen_privstuff_t triangular_lattice_gen_privstuff_t
;
899 points2d_rordered_t ps
;
900 TriangularLatticeOrientation orientation
;
901 double a
; // lattice vector length
903 // not sure if needed:
904 bool includes_origin
;
906 // Denotes an offset of the "origin" point; meaning step hexshift * a / sqrt(2) upwards
907 // or leftwards for the horizontal or vertical orientations, respectively.
911 triangular_lattice_gen_privstuff_t
*priv
;
913 } triangular_lattice_gen_t
;
915 triangular_lattice_gen_t
*triangular_lattice_gen_init(double a
, TriangularLatticeOrientation ori
, bool include_origin
,
917 const points2d_rordered_t
* triangular_lattice_gen_getpoints(const triangular_lattice_gen_t
*g
);
918 int triangular_lattice_gen_extend_to_r(triangular_lattice_gen_t
*g
, double r
);
919 int triangular_lattice_gen_extend_to_steps(triangular_lattice_gen_t
*g
, int maxsteps
);
920 void triangular_lattice_gen_free(triangular_lattice_gen_t
*g
);
929 points2d_rordered_t ps
;
930 TriangularLatticeOrientation orientation
;
935 triangular_lattice_gen_t
*tg
;
936 } honeycomb_lattice_gen_t
;
938 honeycomb_lattice_gen_t
*honeycomb_lattice_gen_init_h(double h
, TriangularLatticeOrientation ori
);
939 honeycomb_lattice_gen_t
*honeycomb_lattice_gen_init_a(double a
, TriangularLatticeOrientation ori
);
940 int honeycomb_lattice_gen_extend_to_steps(honeycomb_lattice_gen_t
*g
, int maxsteps
);
941 int honeycomb_lattice_gen_extend_to_r(honeycomb_lattice_gen_t
*g
, double r
);
942 void honeycomb_lattice_gen_free(honeycomb_lattice_gen_t
*g
);