enumerate-balanced-ideals/weyl.c
2017-02-21 12:01:47 +01:00

818 lines
24 KiB
C

#include "weyl.h"
#include "queue.h"
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#define BIT(n) ((uint64_t)1 << (n))
typedef struct {
weylid_t id;
int position;
} weylid_lookup_t;
static void generate_left_and_ids(semisimple_type_t type, weylgroup_element_t *group);
static int search(const void *key, const void *base, size_t nmem, size_t size, int (*compar) (const void *, const void *, void *), void *arg);
static int compare_root_vectors(int rank, const int *x, const int *y);
static int compare_root_vectors_qsort(const void *x, const void *y, void *arg);
static int compare_weylid(const void *x, const void *y);
static int compare_weylid_lookup(const void *x, const void *y);
static int lookup_id(weylid_t id, weylid_lookup_t *list, int len);
static weylid_t multiply_generator(int s, weylid_t w, const int *simple, const int *mapping, int rank, int positive);
static void reflect_root_vector(const int *cartan, int rank, int i, int *old, int *new);
static weylgroup_element_t* apply_word(int *word, int len, weylgroup_element_t *current);
static weylgroup_element_t* apply_word_reverse(int *word, int len, weylgroup_element_t *current);
/******** generate_left_and_ids and a pile of helper functions **************/
static void generate_left_and_ids(semisimple_type_t type, weylgroup_element_t *group)
{
int rank = weyl_rank(type);
int order = weyl_order(type);
int positive = weyl_positive(type);
queue_t queue;
int current;
int roots_known, elements, length_elements, nextids_count;
int *cartan_matrix;
int *root_vectors;
int *vector;
int *simple_roots;
int *root_mapping;
weylid_t *ids, *edges, *nextids;
weylid_lookup_t *lookup;
// allocate temporary stuff
cartan_matrix = (int*)malloc(rank*rank *sizeof(int));
root_vectors = (int*)malloc(2*positive*rank*sizeof(int));
vector = (int*)malloc(rank *sizeof(int));
root_mapping = (int*)malloc(positive*rank *sizeof(int));
simple_roots = (int*)malloc(rank *sizeof(int));
ids = (weylid_t*)malloc(order *sizeof(weylid_t));
edges = (weylid_t*)malloc(rank*order *sizeof(weylid_t));
nextids = (weylid_t*)malloc(rank*order *sizeof(weylid_t));
lookup = (weylid_lookup_t*)malloc(order *sizeof(weylid_lookup_t));
// get all information on the cartan type
LOG("Get Cartan matrix.\n");
weyl_cartan_matrix(type, cartan_matrix);
// enumerate roots, first the simple ones, then all others by reflecting
LOG("Enumerate roots.\n");
memset(root_vectors, 0, 2*positive*rank*sizeof(int));
roots_known = 0;
queue_init(&queue);
for(int i = 0; i < rank; i++) {
root_vectors[rank*i + i] = 1; // (r_i)_j = delta_ij
queue_put(&queue, i);
roots_known++;
}
while((current = queue_get(&queue)) != -1) {
for(int i = 0; i < rank; i++) {
reflect_root_vector(cartan_matrix, rank, i, &root_vectors[rank*current], vector);
int j;
for(j = 0; j < roots_known; j++)
if(compare_root_vectors(rank, &root_vectors[rank*j], vector) == 0)
break;
if(j == roots_known) {
memcpy(&root_vectors[rank*roots_known], vector, rank*sizeof(int));
queue_put(&queue, roots_known);
roots_known++;
}
}
}
ERROR(roots_known != 2*positive, "Number of roots does not match!\n");
// sort roots and restrict to positives
LOG("Sort roots.\n");
qsort_r(root_vectors, 2*positive, rank*sizeof(int), compare_root_vectors_qsort, &rank);
memcpy(root_vectors, &root_vectors[positive*rank], positive*rank*sizeof(int)); // this just copies the second part of the list onto the first; source and destination are disjoint!
// generate root_mapping, which gives the action of root reflections on positive roots (-1 if result is not a positive root)
LOG("Compute root reflections.\n");
for(int i = 0; i < positive; i++) {
for(int j = 0; j < rank; j++) {
reflect_root_vector(cartan_matrix, rank, j, &root_vectors[rank*i], vector);
root_mapping[i*rank+j] =
search(vector, root_vectors, positive, rank*sizeof(int), compare_root_vectors_qsort, &rank);
}
}
// find simple roots in the list
LOG("Find simple roots.\n");
for(int i = 0; i < rank; i++) {
memset(vector, 0, rank*sizeof(int));
vector[i] = 1;
simple_roots[i] = search(vector, root_vectors, positive, rank*sizeof(int), compare_root_vectors_qsort, &rank);
}
// enumerate weyl group elements using difference sets
LOG("Enumerate Weyl group elements.\n");
nextids[0] = 0;
nextids_count = 1;
elements = 0;
for(int len = 0; len <= positive; len++) {
length_elements = 0;
// find unique ids in edges added in the last iteration
qsort(nextids, nextids_count, sizeof(weylid_t), compare_weylid);
for(int i = 0; i < nextids_count; i++)
if(i == 0 || nextids[i] != nextids[i-1])
ids[elements + length_elements++] = nextids[i];
// add new edges
nextids_count = 0;
for(int i = elements; i < elements + length_elements; i++)
for(int j = 0; j < rank; j++) {
edges[i*rank+j] = multiply_generator(j, ids[i], simple_roots, root_mapping, rank, positive);
if(!(ids[i] & BIT(simple_roots[j]))) // the new element is longer then the old one
nextids[nextids_count++] = edges[i*rank+j];
}
elements += length_elements;
}
// translate the ids to list positions (i.e. local continuous ids)
LOG("Reorder Weyl group elements.\n");
for(int i = 0; i < order; i++) {
lookup[i].id = ids[i];
lookup[i].position = i;
}
qsort(lookup, order, sizeof(weylid_lookup_t), compare_weylid_lookup);
// fill in results
LOG("Compute left multiplication.\n");
for(int i = 0; i < order; i++) {
group[i].id = ids[i];
for(int j = 0; j < rank; j++)
group[i].left[j] = group + lookup_id(edges[i*rank+j], lookup, order);
}
// free temporary stuff
free(cartan_matrix);
free(root_vectors);
free(vector);
free(root_mapping);
free(simple_roots);
free(ids);
free(edges);
free(nextids);
free(lookup);
}
// glibc search function, but with user pointer and returning index (or -1 if not found)
static int search(const void *key, const void *base, size_t nmemb, size_t size, int (*compar) (const void *, const void *, void *), void *arg)
{
size_t l, u, idx;
const void *p;
int comparison;
l = 0;
u = nmemb;
while (l < u) {
idx = (l + u) / 2;
p = (void *) (((const char *) base) + (idx * size));
comparison = (*compar) (key, p, arg);
if (comparison < 0)
u = idx;
else if (comparison > 0)
l = idx + 1;
else
return idx;
}
return -1;
}
// maybe we want a different ordering here?
static int compare_root_vectors(int rank, const int *x, const int *y)
{
for(int i = 0; i < rank; i++)
if(x[i] != y[i])
return x[i] - y[i];
return 0;
}
static int compare_root_vectors_qsort(const void *x, const void *y, void *arg)
{
return compare_root_vectors(*((int*)arg), x, y);
}
static int compare_weylid(const void *x, const void *y)
{
weylid_t u = *((weylid_t*)x);
weylid_t v = *((weylid_t*)y);
return u > v ? 1 : u < v ? -1 : 0;
}
static int compare_weylid_lookup(const void *x, const void *y)
{
weylid_t u = ((weylid_lookup_t*)x)->id;
weylid_t v = ((weylid_lookup_t*)y)->id;
return u > v ? 1 : u < v ? -1 : 0;
}
static int lookup_id(weylid_t id, weylid_lookup_t *list, int len)
{
weylid_lookup_t key;
key.id = id;
weylid_lookup_t *p = (weylid_lookup_t*)bsearch(&key, list, len, sizeof(weylid_lookup_t), compare_weylid_lookup);
return p->position;
}
static weylid_t multiply_generator(int s, weylid_t w, const int* simple, const int* mapping, int rank, int positive)
{
weylid_t sw = 0;
for(int i = 0; i < positive; i++) {
if(w & BIT(i))
if(mapping[i*rank+s] != -1)
sw |= BIT(mapping[i*rank+s]);
}
if(w & BIT(simple[s]))
return sw;
else
return sw | BIT(simple[s]);
}
static void reflect_root_vector(const int *cartan, int rank, int i, int *old, int *new)
{
memcpy(new, old, rank*sizeof(int));
for(int j = 0; j < rank; j++)
new[i] -= cartan[i*rank + j]*old[j];
}
/************* Weyl group infos ************************/
static int weyl_exists(simple_type_t type)
{
if(type.series < 'A' || type.series > 'G' || type.rank < 1 ||
type.series == 'B' && type.rank < 2 ||
type.series == 'C' && type.rank < 2 ||
type.series == 'D' && type.rank < 3 ||
type.series == 'E' && type.rank != 6 && type.rank != 7 && type.rank != 8 ||
type.series == 'F' && type.rank != 4 ||
type.series == 'G' && type.rank != 2)
return 0;
else
return 1;
}
int weyl_rank(semisimple_type_t type)
{
int rank = 0;
for(int i = 0; i < type.n; i++)
rank += type.factors[i].rank;
return rank;
}
int weyl_order(semisimple_type_t type)
{
int order = 1;
for(int i = 0; i < type.n; i++) {
ERROR(!weyl_exists(type.factors[i]), "A Weyl group of type %c%d does not exist!\n", type.factors[i].series, type.factors[i].rank);
switch(type.factors[i].series) {
case 'A':
for(int j = 1; j <= type.factors[i].rank + 1; j++)
order *= j;
break;
case 'B': case 'C':
for(int j = 1; j <= type.factors[i].rank; j++)
order *= 2*j;
break;
case 'D':
for(int j = 2; j <= type.factors[i].rank; j++)
order *= 2*j;
break;
case 'E':
if(type.factors[i].rank == 6)
order *= 51840;
else if(type.factors[i].rank == 7)
order *= 2903040;
else if(type.factors[i].rank == 8)
order *= 696729600;
break;
case 'F':
order *= 1152;
break;
case 'G':
order *= 12;
break;
}
}
return order;
}
int weyl_positive(semisimple_type_t type)
{
int positive = 0;
for(int i = 0; i < type.n; i++) {
ERROR(!weyl_exists(type.factors[i]), "A Weyl group of type %c%d does not exist!\n", type.factors[i].series, type.factors[i].rank);
switch(type.factors[i].series) {
case 'A':
positive += (type.factors[i].rank * (type.factors[i].rank + 1)) / 2;
break;
case 'B': case 'C':
positive += type.factors[i].rank * type.factors[i].rank;
break;
case 'D':
positive += type.factors[i].rank * (type.factors[i].rank - 1);
break;
case 'E':
if(type.factors[i].rank == 6)
positive += 36;
else if(type.factors[i].rank == 7)
positive += 63;
else if(type.factors[i].rank == 8)
positive += 120;
break;
case 'F':
positive += 24;
break;
case 'G':
positive += 6;
break;
}
}
return positive;
}
int weyl_opposition(semisimple_type_t type, int simple_root)
{
int offset = 0;
int factor = 0;
int r, iota_r;
for(factor = 0; factor < type.n; factor++)
if(simple_root < offset + type.factors[factor].rank)
break;
else
offset += type.factors[factor].rank;
r = simple_root - offset;
ERROR(!weyl_exists(type.factors[factor]), "A Weyl group of type %c%d does not exist!\n", type.factors[factor].series, type.factors[factor].rank);
switch(type.factors[factor].series) {
case 'B': case 'C': case 'F': case 'G':
iota_r = r;
break;
case 'A':
iota_r = type.factors[factor].rank - 1 - r;
break;
case 'D':
if(type.factors[factor].rank % 2 == 0)
iota_r = r;
else
iota_r = r == 0 ? 1 : r == 1 ? 0 : r;
break;
case 'E':
if(type.factors[factor].rank == 6)
iota_r = r;
else
iota_r = r == 3 || r == 4 ? r : 5 - r;
break;
}
return iota_r + offset;
}
void weyl_cartan_matrix(semisimple_type_t type, int *m)
{
int offset = 0;
int rank = weyl_rank(type);
int **A = (int**)malloc(rank*sizeof(int*));
memset(m, 0, rank*rank*sizeof(int));
for(int i = 0; i < rank; i++)
m[i*rank+i] = 2;
for(int k = 0; k < type.n; k++) {
ERROR(!weyl_exists(type.factors[k]), "A Weyl group of type %c%d does not exist!\n", type.factors[k].series, type.factors[k].rank);
for(int i = 0; i < type.factors[k].rank; i++) // A is the submatrix corresponding to the current simple factor
A[i] = &m[(i+offset)*rank + offset];
for(int i = 1; i < type.factors[k].rank; i++) {
A[i][i-1] = -1;
A[i-1][i] = -1;
}
switch(type.factors[k].series) {
case 'A':
break;
case 'B':
A[0][1] = -2;
break;
case 'C':
A[1][0] = -2;
break;
case 'D':
A[0][1] = A[1][0] = 0;
A[0][2] = A[2][0] = -1;
break;
case 'E':
A[1][2] = A[2][1] = 0;
A[1][3] = A[3][1] = -1;
break;
case 'F':
A[2][1] = -2;
break;
case 'G':
A[1][0] = -3;
break;
}
offset += type.factors[k].rank;
}
free(A);
}
/************ weyl_generate etc. ********************/
static weylgroup_element_t* apply_word(int *word, int len, weylgroup_element_t *current)
{
for(int k = len - 1; k >= 0; k--) // apply group element from right to left
current = current->left[word[k]];
return current;
}
static weylgroup_element_t* apply_word_reverse(int *word, int len, weylgroup_element_t *current)
{
for(int k = 0; k < len; k++) // apply group element from left to right (i.e. apply inverse)
current = current->left[word[k]];
return current;
}
weylgroup_t *weyl_generate(semisimple_type_t type)
{
int rank = weyl_rank(type);
int order = weyl_order(type);
int positive = weyl_positive(type);
ERROR(positive > 64, "We can't handle root systems with more than 64 positive roots!\n");
// allocate result
weylgroup_element_t *group = (weylgroup_element_t*)malloc(order*sizeof(weylgroup_element_t));
weylgroup_t *result = malloc(sizeof(weylgroup_t));
result->type = type;
result->elements = group;
result->lists = (weylgroup_element_t**)malloc(2*order*rank*sizeof(weylgroup_element_t*));
for(int i = 0; i < order; i++) {
group[i].left = result->lists + 2*i*rank;
group[i].right = result->lists + (2*i+1)*rank;
group[i].coset = (doublecoset_t*)0;
group[i].index = i;
}
// the main part
LOG("Start generating Weyl group.\n");
generate_left_and_ids(type, group);
// word length is just the number of 1s in the binary id
LOG("Find word lengths.\n");
for(int i = 0; i < order; i++) {
group[i].wordlength = 0;
for(int j = 0; j < positive; j++)
if(group[i].id & BIT(j))
group[i].wordlength++;
}
// allocate letters
int total_wordlength = 0;
for(int i = 0; i < order; i++)
total_wordlength += group[i].wordlength;
result->letters = (int*)malloc(total_wordlength*sizeof(int));
total_wordlength = 0;
for(int i = 0; i < order; i++) {
group[i].word = result->letters + total_wordlength;
total_wordlength += group[i].wordlength;
}
// find shortest words (using that the elements are already ordered by word length)
LOG("Find shortest words.\n");
memset(result->letters, -1, total_wordlength*sizeof(int));
for(int i = 0; i < order - 1; i++) {
weylgroup_element_t *this = &group[i];
for(int j = 0; j < rank; j++) {
weylgroup_element_t *that = group[i].left[j];
if(that->wordlength > this->wordlength && that->word[0] == -1) {
memcpy(that->word + 1, this->word, this->wordlength*sizeof(int));
that->word[0] = j;
}
}
}
// generate right edges
LOG("Compute right multiplication.\n");
for(int i = 0; i < order; i++)
for(int j = 0; j < rank; j++)
group[i].right[j] = apply_word(group[i].word, group[i].wordlength, group[0].left[j]);
// find opposites
LOG("Find opposites.\n");
weylgroup_element_t *longest = &group[order-1];
for(int i = 0; i < order; i++)
group[i].opposite = apply_word(longest->word, longest->wordlength, &group[i]);
// check for root reflections
LOG("Find root reflections.\n");
for(int i = 0; i < order; i++)
group[i].is_root_reflection = 0;
for(int i = 0; i < order; i++)
for(int j = 0; j < rank; j++) // we want to calculate word^{-1} * j * word; this is a root reflection
apply_word_reverse(group[i].word, group[i].wordlength, group[i].left[j]) -> is_root_reflection = 1;
return result;
}
void weyl_destroy(weylgroup_t *group)
{
free(group->elements);
free(group->lists);
free(group->letters);
free(group);
}
doublequotient_t *weyl_generate_bruhat(semisimple_type_t type, int left_invariance, int right_invariance)
{
int rank = weyl_rank(type);
int order = weyl_order(type);
int positive = weyl_positive(type);
int count;
int is_minimum, is_maximum;
weylgroup_t *wgroup = weyl_generate(type);
weylgroup_element_t *group = wgroup->elements;
doublecoset_t *cosets;
for(int i = 0; i < rank; i++) {
int oppi = weyl_opposition(type, i);
if(left_invariance & BIT(i) && !(left_invariance & BIT(oppi)) ||
left_invariance & BIT(oppi) && !(left_invariance & BIT(i)))
ERROR(1, "The specified left invariance is not invariant under the opposition involution!\n");
}
doublequotient_t *result = (doublequotient_t*)malloc(sizeof(doublequotient_t));
result->type = type;
result->left_invariance = left_invariance;
result->right_invariance = right_invariance;
result->group = wgroup->elements;
result->grouplists = wgroup->lists;
result->groupletters = wgroup->letters;
free(wgroup); // dissolved in result and not needed anymore
LOG("Count cosets.\n"); // count cosets by finding the minimum length element in every coset
count = 0;
for(int i = 0; i < order; i++) {
is_minimum = 1;
for(int j = 0; j < rank; j++)
if(left_invariance & BIT(j) && group[i].left[j]->wordlength < group[i].wordlength ||
right_invariance & BIT(j) && group[i].right[j]->wordlength < group[i].wordlength)
is_minimum = 0;
if(is_minimum)
count++;
}
result->count = count;
// alloc more stuff
cosets = result->cosets = (doublecoset_t*)malloc(count*sizeof(doublecoset_t));
for(int i = 0; i < count; i++) {
cosets[i].bruhat_lower = cosets[i].bruhat_higher = (doublecoset_list_t*)0;
}
result->lists = (doublecoset_list_t*)malloc(2*count*positive*sizeof(doublecoset_list_t)); // 2 times, for bruhat lower and higher
LOG("Find minimal length elements in cosets.\n"); // basically same code as above
count = 0;
for(int i = 0; i < order; i++) {
is_minimum = 1;
for(int j = 0; j < rank; j++)
if(left_invariance & BIT(j) && group[i].left[j]->wordlength < group[i].wordlength ||
right_invariance & BIT(j) && group[i].right[j]->wordlength < group[i].wordlength)
is_minimum = 0;
if(is_minimum) {
cosets[count].min = &group[i];
group[i].coset = &cosets[count];
count++;
}
}
LOG("Generate quotient map.\n");
for(int i = 0; i < order; i++) {
for(int j = 0; j < rank; j++) {
if(left_invariance & BIT(j) && group[i].left[j]->wordlength > group[i].wordlength)
group[i].left[j]->coset = group[i].coset;
if(right_invariance & BIT(j) && group[i].right[j]->wordlength > group[i].wordlength)
group[i].right[j]->coset = group[i].coset;
}
}
LOG("Find maximal length elements.\n");
for(int i = 0; i < order; i++) {
is_maximum = 1;
for(int j = 0; j < rank; j++)
if(left_invariance & BIT(j) && group[i].left[j]->wordlength > group[i].wordlength ||
right_invariance & BIT(j) && group[i].right[j]->wordlength > group[i].wordlength)
is_maximum = 0;
if(is_maximum) {
group[i].coset->max = &group[i];
}
}
LOG("Find opposites.\n");
for(int i = 0; i < count; i++)
cosets[i].opposite = cosets[i].min->opposite->coset;
LOG("Sort opposites.\n");
int *old2newindices = (int*)malloc(count*sizeof(int));
int *new2oldindices = (int*)malloc(count*sizeof(int));
// give the cosets some temporary indices
for(int i = 0; i < count; i++)
cosets[i].index = i;
// generate a nice ordering, where element j is opposite to n-j, except the self-opposite ones, which are in the middle
int j = 0;
for(int i = 0; i < count; i++)
if(i < cosets[i].opposite->index) {
old2newindices[i] = j;
old2newindices[cosets[i].opposite->index] = count-1-j;
j++;
}
for(int i = 0; i < count; i++)
if(i == cosets[i].opposite->index)
old2newindices[i] = j++;
for(int i = 0; i < count; i++)
new2oldindices[old2newindices[i]] = i;
// rewrite everything in the new ordering
doublecoset_t *oldcosets = (doublecoset_t*)malloc(count*sizeof(doublecoset_t));
memcpy(oldcosets, cosets, count*sizeof(doublecoset_t));
for(int i = 0; i < count; i++) {
cosets[i].min = oldcosets[new2oldindices[i]].min;
cosets[i].max = oldcosets[new2oldindices[i]].max;
cosets[i].opposite = cosets + old2newindices[oldcosets[new2oldindices[i]].opposite->index];
// cosets[i].bruhat_lower = oldcosets[new2oldindices[i]].bruhat_lower;
// cosets[i].bruhat_higher = oldcosets[new2oldindices[i]].bruhat_higher;
// for(doublecoset_list_t *current = cosets[i].bruhat_lower; current; current = current -> next)
// current->to = &cosets[old2newindices[current->to->index]];
// for(doublecoset_list_t *current = cosets[i].bruhat_higher; current; current = current -> next)
// current->to = &cosets[old2newindices[current->to->index]];
}
for(int i = 0; i < order; i++)
group[i].coset = old2newindices[group[i].coset->index] + cosets;
for(int i = 0; i < count; i++) // do this in the end, so we can use the "index" attribute before to translate pointers to indices
cosets[i].index = i;
free(old2newindices);
free(new2oldindices);
free(oldcosets);
LOG("Find bruhat order.\n");
int edgecount = 0;
for(int i = 0; i < order; i++) {
if(group[i].is_root_reflection) {
for(int j = 0; j < count; j++) {
weylgroup_element_t *this = cosets[j].min;
weylgroup_element_t *that = apply_word(group[i].word, group[i].wordlength, cosets[j].min);
if(this->wordlength > that->wordlength) { // this is higher in bruhat order than that
doublecoset_list_t *new = &result->lists[edgecount++];
new->next = this->coset->bruhat_lower;
this->coset->bruhat_lower = new;
new->to = that->coset;
}
}
}
}
LOG("Perform transitive reduction.\n"); // eliminating redudant order relations
doublecoset_t *origin;
doublecoset_list_t *current;
doublecoset_list_t *prev;
queue_t queue;
int idx;
int *seen = malloc(count*sizeof(int));
for(int i = 0; i < count; i++) {
memset(seen, 0, count*sizeof(int));
queue_init(&queue);
for(int len = 1; len <= cosets[i].min->wordlength; len++) {
// remove all edges originating from i of length len which connect to something already seen using shorter edges
origin = &cosets[i];
prev = (doublecoset_list_t*)0;
for(current = origin->bruhat_lower; current; current = current->next) {
if(origin->min->wordlength - current->to->min->wordlength != len) {
prev = current;
} else if(seen[current->to->index]) {
if(prev)
prev->next = current->next;
else
origin->bruhat_lower = current->next;
} else {
prev = current;
seen[current->to->index] = 1;
queue_put(&queue, current->to->index);
}
}
// see which nodes we can reach using only edges up to length len, mark them as seen
while((idx = queue_get(&queue)) != -1) {
current = cosets[idx].bruhat_lower;
for(current = cosets[idx].bruhat_lower; current; current = current->next) {
if(!seen[current->to->index]) {
seen[current->to->index] = 1;
queue_put(&queue, current->to->index);
}
}
}
}
}
free(seen);
LOG("Revert bruhat order.\n");
for(int i = 0; i < count; i++) {
for(current = cosets[i].bruhat_lower; current; current = current->next) {
doublecoset_list_t *new = &result->lists[edgecount++];
new->to = &cosets[i];
new->next = current->to->bruhat_higher;
current->to->bruhat_higher = new;
}
}
return result;
}
void weyl_destroy_bruhat(doublequotient_t *dq)
{
free(dq->group);
free(dq->grouplists);
free(dq->groupletters);
free(dq->cosets);
free(dq->lists);
free(dq);
}