From c3a89b24adc5bc370531627eada46b2d60f08186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ne=C4=8Dada?= Date: Wed, 14 Nov 2018 15:13:16 +0200 Subject: [PATCH] Abstract lattice point generators Former-commit-id: 7c2d5d2a94f2888150ea2bad79d4e287a3323061 --- qpms/latticegens.c | 109 +++++++++++++++++++++++++++++++++++++++++++++ qpms/lattices.h | 77 ++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 qpms/latticegens.c diff --git a/qpms/latticegens.c b/qpms/latticegens.c new file mode 100644 index 0000000..1233e6f --- /dev/null +++ b/qpms/latticegens.c @@ -0,0 +1,109 @@ +#include "lattices.h" +// here, various "classes" of the PGenSph point generators are implemented. + + +// const PGenSphReturnData PGenSphDoneVal = {PGEN_DONE, {0,0,0}}; // defined already in lattices.h + +// General structure of a generator implementation looks like this: + +#if 0 +//==== PGenSph_NAME ==== + +extern const PGenSphClassInfo PGenSph_NAME; // forward declaration needed by constructor (may be placed in header file instead) + +// Internal state structure +typedef struct PGenSph_NAME_StateData { + ... +} PGenSph_NAME_StateData; + +// Constructor +PGenSph PGenSph_NAME_new(...) { + g->stateData = malloc(sizeof(PGenSph_NAME_StateData)); + ... + PGenSph g = {&PGenSph_NAME, (void *) stateData}; + return g; +} + +// Dectructor +void PGenSph_NAME_dectructor(PGenSph *g) { + ... + free(g->stateData); + g->stateData = NULL; +} + +// Extractor +PGenSphReturnData PGenSph_NAME_next(PGenSph *g) { + if (g->stateData == NULL) // already destroyed + return PGenSphDoneVal; + else { + PGenSph_NAME_StateData *s = (PGenSph_NAME_StateData *) g->stateData; + if (... /* there are still points to be generated */) { + ... + PGenSphReturnData retval = {.../*flags*/, .../*thePoint*/}; + return retval; + } else { + PGenSph_destroy(g); + return PGenSphDoneVal; + } + } +} + +// Class metadata structure; TODO maybe this can rather be done by macro. +const PGenSphClassInfo PGenSph_NAME = { + "PGenSph_NAME", + PGenSph_NAME_next, + PGenSph_NAME_destructor +}; + +#endif // 0 + + +//==== PGenSph_FromPoint2DArray ==== + +// Internal state structure +typedef struct PGenSph_FromPoint2DArray_StateData { + const point2d *base; + size_t len; + size_t currentIndex; +}PGenSph_FromPoint2DArray_StateData; + +// Constructor +PGenSph PGenSph_FromPoint2DArray_new(const point2d *points, size_t len) { + PGenSph_FromPoint2DArray_StateData *stateData = malloc(sizeof(PGenSph_FromPoint2DArray_StateData)); + stateData->base = points; + stateData->len = len; + stateData->currentIndex = 0; + PGenSph g = {&PGenSph_FromPoint2DArray, (void *) stateData}; + return g; +} + +// Destructor +void PGenSph_FromPoint2DArray_destructor(PGenSph *g) { + free(g->stateData); + g->stateData = NULL; +} + +// Extractor +PGenSphReturnData PGenSph_FromPoint2DArray_next(PGenSph *g) { + if (g->stateData == NULL) // already destroyed + return PGenSphDoneVal; + else { + PGenSph_FromPoint2DArray_StateData *s = (PGenSph_FromPoint2DArray_StateData *) g->stateData; + if (s->currentIndex < s->len) { + sph_t thePoint = cart22sph(s->base[s->currentIndex]); + ++(s->currentIndex); + PGenSphReturnData retval = {(PGEN_AT_XY | PGEN_NEWR), thePoint}; + return retval; + } else { + PGenSph_destroy(g); + return PGenSphDoneVal; + } + } +} + +const PGenSphClassInfo PGenSph_FromPoint2DArray = { + "PGenSph_FromPoint2DArray", + PGenSph_FromPoint2DArray_next, + PGenSph_FromPoint2DArray_destructor, +}; + diff --git a/qpms/lattices.h b/qpms/lattices.h index b9a7421..a5efd2f 100644 --- a/qpms/lattices.h +++ b/qpms/lattices.h @@ -33,6 +33,83 @@ static inline point2d point2d_fromxy(const double x, const double y) { return p; } + + +/* + * GENERIC LATTICE POINT GENERATOR TYPE PGenSph + * ============================================ + * + * A bit of OOP-in-C brainfuck here. + * + * The basic principle of operation is following: + * Instead of a list (array) of points, an initialized PGenSph object + * is passed to a function that does something over a set of points. + * Each time PGenSph-type object is "called", it returns PGenSphReturnData, + * which contains a point in spherical coordinates (sph_t) and some metadata. + * + * After the last generated point, the generator frees all internal memory + * and returns PGenSphReturnData with PGEN_NOTDONE flag unset (the rest + * shall be considered invalid data). + * The caller can also decide not to use the rest and end getting the points + * even when the PGEN_NOTDONE was set in the last returned data. + * In such case, the caller shall call PGenSph_destroy() manually. + * + * MEMORY MANAGEMENT POLICY + * ------------------------ + * The basic PGenSph structure shall be allocated on stack (it's only two pointers), + * everything internal goes on heap. + */ + +struct PGenSph; // full definition below + +typedef enum PGenPointFlags { + PGEN_NOTDONE = 2, // The most important flag: when this is not set, the interation ended – other data returned should be considered nonsense and at this point, the generator should have de-allocated all internal memory. + PGEN_NEWR = 1, // The r-coordinate is different than in the previous generated point (so radial parts of the calculation have to be redone); + PGEN_AT_Z = 4, // This is set if we are at the z-axis (theta is either 0 or M_PI) + PGEN_AT_XY = 8, // This is set if we are at the xy-plane (theta is M_PI2) + PGEN_DONE = 0, // convenience value, not an actual flag +} PGenPointFlags; + +typedef struct PGenSphReturnData { + PGenPointFlags flags; // metatada + sph_t point_sph; // the actual point data +} PGenSphReturnData; + +static const PGenSphReturnData PGenSphDoneVal = {PGEN_DONE, {0,0,0}}; // convenience constant for use in the exctractor implementations + +typedef struct PGenSphClassInfo { // static PGenSph info + char * const name; // mainly for debugging purposes + PGenSphReturnData (*next)(struct PGenSph *); // This contains the actual generator procedure (TODO shouldn't I rather point to stateData?) + void (*destructor)(struct PGenSph *); // Destructor to be called by next() at iteration end, or by the caller if ending the generation prematurely +} PGenSphClassInfo; + +// TOP DATA STRUCTURE DEFINITION HERE +typedef struct PGenSph { + const PGenSphClassInfo * const c; + void *stateData; // shall be NULL if invalid (destroyed) +} PGenSph; + +static inline void PGenSph_destroy(PGenSph *g) { + g->c->destructor(g); + assert(g->stateData == NULL); // this should be done by the destructor +} + +static inline PGenSphReturnData PGenSph_next(PGenSph *g) { + // TODO maybe some asserts around here + return g->c->next(g); +} + + +/* + * Some basic lattice generators implementing the abstract interface above (implemented in latticegens.c). + */ + +// This one simply iterates over an existing array of Point2d +extern const PGenSphClassInfo PGenSph_FromPoint2DArray; // TODO Do I even need this to be declared here? +PGenSph PGenSph_FromPoints2DArray_new(const point2d *points, size_t len); + + + /* * THE NICE PART (adaptation of lattices2d.py) * ===========================================