/* file fsamicomp.c 12. 10. 95.
* 29/9/98 large-scale re-organisation.
* 13/1/98 changes for `gen' type for generators instead of char.
* 8.3.97. Small bug (on NeXT) corrected
* 10.1.96. Added function fsa_mimult2().
* 31.10.95. Added function fsa_migm2().
* This file contains the routines for forming the composite multiplier
* fsa's for coset automatic group with multiple initial states.
* These are used to derive defining relators for the subgroup.
*/
#include <stdio.h>
#include "defs.h"
#include "fsa.h"
#include "hash.h"
#include "externals.h"
#define LABHTSIZE 8192
/* Functions defined in this file: */
static fsa *fsa_migm2_short(fsa *migmptr, storage_type op_table_type,
boolean destroy, char *migm2filename,
boolean readback, char *prefix);
static fsa *fsa_migm2_int(fsa *migmptr, storage_type op_table_type,
boolean destroy, char *migm2filename,
boolean readback, char *prefix);
static fsa *fsa_micomposite_short(fsa *mult1ptr, fsa *mult2ptr,
storage_type op_table_type, boolean destroy,
char *compfilename, boolean readback);
static fsa *fsa_micomposite_int(fsa *mult1ptr, fsa *mult2ptr,
storage_type op_table_type, boolean destroy,
char *compfilename, boolean readback);
/* This is the minimization function for composite multipliers
* for coset automatic groups.
* These are multiple initial state machines, and the states should be
* labeled, with the initial states having separate labels.
* The labels will be lists of words, containing a single word initially.
* During minimization those initial states which cannot lead to success
* cease to be initial. If some of the other initial states become
* amalgamated, then their labels (which represent equal elements in
* the subgroup) will be concatenated.
*/
int mimult_minimize(fsa *fsaptr)
{
int *block_numa, *block_numb, *block_swap, ct, ss, sst, *gotlist, i, j, k, l,
m, len = 0, *ptr, *ptre, *ptr2, *ptr2e, *ht_ptr, ni, ne, ns_orig, **table,
ns_final, ns_new, num_iterations, nlab, *lablen;
hash_table ht;
boolean fixed, *got, ok, acc;
gen ***newlabels, ***oldlabels;
setToLabelsType *labno;
if (fsaptr->table->table_type == SPARSE && fsaptr->table->denserows > 0) {
fprintf(stderr, "Sorry: mimult_minimize unavailable for sparse storage "
"with dense rows.\n");
return -1;
}
if (kbm_print_level >= 3)
printf(" #Calling mimult_minimize.\n");
if (!fsaptr->flags[MIDFA]) {
fprintf(stderr, "Error: mimult_minimize only applies to MIDFA's.\n");
return -1;
}
if (fsaptr->states->type != LABELED) {
fprintf(stderr,
"Error: in mimult_minimize state-set must have type labeled.\n");
return -1;
}
acc = fsaptr->flags[ACCESSIBLE] || fsaptr->flags[TRIM];
ne = fsaptr->alphabet->size;
table = fsaptr->table->table_data_ptr;
ns_orig = fsaptr->states->size;
if (ns_orig == 0)
return 0;
/* We now test to see which start-states can lead to success.
* Those that cannot will cease to be start-states.
*/
fsa_set_is_accepting(fsaptr);
fsa_set_is_initial(fsaptr);
tmalloc(gotlist, int, ns_orig + 1);
tmalloc(got, boolean, ns_orig + 1);
for (ss = 1; ss <= fsaptr->num_initial; ss++) {
sst = fsaptr->initial[ss];
if (fsaptr->is_accepting[sst])
continue;
ok = FALSE;
for (i = 1; i <= ns_orig; i++)
got[i] = FALSE;
got[sst] = TRUE;
gotlist[1] = sst;
ct = 1;
for (i = 1; i <= ct; i++) {
k = gotlist[i];
if (fsaptr->table->table_type == DENSE)
for (j = 1; j <= ne; j++) {
l = table[j][k];
if (l > 0 && !got[l]) {
if (fsaptr->is_accepting[l]) {
ok = TRUE;
break;
}
got[l] = TRUE;
gotlist[++ct] = l;
}
}
else {
ptr = table[k];
ptre = table[k + 1];
while (ptr < ptre) {
l = *(++ptr);
if (l > 0 && !got[l]) {
if (fsaptr->is_accepting[l]) {
ok = TRUE;
break;
}
got[l] = TRUE;
gotlist[++ct] = l;
}
ptr++;
}
}
}
if (!ok) {
fsaptr->is_initial[sst] = FALSE;
acc = FALSE;
}
}
tfree(gotlist);
tfree(got);
ct = 0;
for (i = 1; i <= ns_orig; i++)
if (fsaptr->is_initial[i])
ct++;
if (ct != fsaptr->num_initial) {
fsaptr->num_initial = ct;
tfree(fsaptr->initial);
tmalloc(fsaptr->initial, int, ct + 1);
ct = 0;
for (i = 1; i <= ns_orig; i++)
if (fsaptr->is_initial[i])
fsaptr->initial[++ct] = i;
}
tfree(fsaptr->is_initial);
tfree(fsaptr->is_accepting);
if (!acc)
fsa_set_is_accessible(fsaptr);
tmalloc(block_numa, int, ns_orig + 1);
tmalloc(block_numb, int, ns_orig + 1);
for (i = 0; i <= ns_orig; i++)
block_numb[i] = 0;
/* Start with block_numa equal to the accept/reject division
* Remember that state/block number 0 is always failure with no hope.
*/
if (fsaptr->num_accepting == ns_orig) {
block_numa[0] = 0;
for (i = 1; i <= ns_orig; i++)
if (acc || fsaptr->is_accessible[i])
block_numa[i] = 1;
else
block_numa[i] = 0;
}
else {
for (i = 0; i <= ns_orig; i++)
block_numa[i] = 0;
for (i = 1; i <= fsaptr->num_accepting; i++)
if (acc || fsaptr->is_accessible[fsaptr->accepting[i]])
block_numa[fsaptr->accepting[i]] = 1;
}
fixed = fsaptr->table->table_type == DENSE;
ns_new = 1;
num_iterations = 0;
/* The main refinement loop follows. */
do {
num_iterations++;
ns_final = ns_new;
/* Turn off excessive printing at this point */
j = kbm_print_level;
kbm_print_level = 1;
hash_init(&ht, fixed, ne + 1, 0, 0);
kbm_print_level = j;
if (kbm_print_level >= 3)
printf(" #Iterating - number of state categories = %d.\n", ns_new);
block_numb[0] = 0;
for (i = 1; i <= ns_orig; i++)
if (acc || fsaptr->is_accessible[i]) {
/* Insert the encoded form of the transitions from state i into the
* hashtable preceded by the current block number of i.
*/
len = fixed ? ne + 1 : table[i + 1] - table[i] + 1;
ptr = ht.current_ptr;
*ptr = block_numa[i];
if (fixed) {
for (j = 1; j < len; j++)
ptr[j] = block_numa[table[j][i]];
l = len;
}
else {
l = 0;
for (j = 1; j < len; j += 2) {
k = block_numa[table[i][j]];
if (k > 0) {
ptr[++l] = table[i][j - 1];
ptr[++l] = k;
}
}
if (l > 0 || *ptr > 0)
l++;
/* For technical reasons, we want the zero record to be empty */
}
block_numb[i] = hash_locate(&ht, l);
}
else
block_numb[i] = 0;
ns_new = ht.num_recs;
block_swap = block_numa;
block_numa = block_numb;
block_numb = block_swap;
if (ns_new > ns_final)
hash_clear(&ht);
} while (ns_new > ns_final);
/* At this stage, either ns_final = ns_new, or the fsa has empty accepted
* language, ns_new=0 and ns_final=1.
*/
fsaptr->flags[ACCESSIBLE] = TRUE;
fsaptr->flags[MINIMIZED] = TRUE;
if (ns_new == 0) {
/* This is the awkward case of no states - always causes problems! */
fsaptr->states->size = 0;
fsaptr->num_initial = 0;
tfree(fsaptr->initial);
fsaptr->num_accepting = 0;
tfree(fsaptr->accepting);
tfree(fsaptr->table->table_data_ptr[0]);
tfree(fsaptr->table->table_data_ptr);
}
else if (ns_final < ns_orig) {
/* Re-define the fsa fields */
fsaptr->states->size = ns_final;
/* sort out the initial states and their labels -
* first calculate how many labels there are and how long they are
*/
ni = fsaptr->num_initial;
tmalloc(lablen, int, ni + 1);
for (i = 1; i <= ni; i++)
lablen[i] = 0;
tmalloc(labno, setToLabelsType, ns_new + 1);
for (i = 1; i <= ns_new; i++)
labno[i] = 0;
nlab = 0;
for (i = 1; i <= ni; i++) {
k = block_numa[fsaptr->initial[i]];
if (labno[k] == 0)
labno[k] = ++nlab;
lablen[labno[k]]++;
}
tmalloc(newlabels, gen **, nlab + 1);
oldlabels = fsaptr->states->labels->wordslist;
for (i = 1; i <= nlab; i++)
tmalloc(newlabels[i], gen *, lablen[i] + 1);
for (i = 1; i <= nlab; i++)
lablen[i] = 0;
for (i = 1; i <= ni; i++) {
j = fsaptr->initial[i];
k = block_numa[j];
l = labno[k];
m = fsaptr->states->setToLabels[j];
tmalloc(newlabels[l][lablen[l]], gen, genstrlen(oldlabels[m][0]) + 1);
genstrcpy(newlabels[l][lablen[l]], oldlabels[m][0]);
lablen[l]++;
}
for (i = 1; i <= nlab; i++)
newlabels[i][lablen[i]] = 0;
for (i = 1; i <= fsaptr->states->labels->size; i++) {
j = 0;
while (oldlabels[i][j]) {
tfree(oldlabels[i][j]);
j++;
}
tfree(oldlabels[i]);
}
tfree(oldlabels);
fsaptr->states->labels->wordslist = newlabels;
fsaptr->states->labels->size = nlab;
tfree(fsaptr->states->setToLabels);
fsaptr->states->setToLabels = labno;
fsaptr->num_initial = nlab;
tfree(fsaptr->initial);
tfree(lablen);
tmalloc(fsaptr->initial, int, nlab + 1);
ct = 0;
for (i = 1; i <= ns_new; i++)
if (labno[i])
fsaptr->initial[++ct] = i;
if (fsaptr->num_accepting == ns_orig) {
fsaptr->num_accepting = ns_final;
if (ns_final == 1) {
tmalloc(fsaptr->accepting, int, 2);
fsaptr->accepting[1] = 1;
}
}
else {
tmalloc(fsaptr->is_accepting, boolean, ns_final + 1);
for (i = 1; i <= ns_final; i++)
fsaptr->is_accepting[i] = FALSE;
for (i = 1; i <= fsaptr->num_accepting; i++)
fsaptr->is_accepting[block_numa[fsaptr->accepting[i]]] = TRUE;
fsaptr->num_accepting = 0;
for (i = 1; i <= ns_final; i++)
if (fsaptr->is_accepting[i])
fsaptr->num_accepting++;
tfree(fsaptr->accepting);
tmalloc(fsaptr->accepting, int, fsaptr->num_accepting + 1);
j = 0;
for (i = 1; i <= ns_final; i++)
if (fsaptr->is_accepting[i])
fsaptr->accepting[++j] = i;
tfree(fsaptr->is_accepting);
}
/* Finally copy the transition table data from the hash-table back to the
* fsa */
tfree(fsaptr->table->table_data_ptr[0]);
tfree(fsaptr->table->table_data_ptr);
if (fixed) {
fsa_table_init(fsaptr->table, ns_final, ne);
table = fsaptr->table->table_data_ptr;
for (i = 1; i <= ns_final; i++) {
ht_ptr = hash_rec(&ht, i);
for (j = 1; j <= ne; j++)
table[j][i] = ht_ptr[j];
}
}
else {
tmalloc(fsaptr->table->table_data_ptr, int *, ns_final + 2);
tmalloc(fsaptr->table->table_data_ptr[0], int, ht.tot_space - ns_final);
table = fsaptr->table->table_data_ptr;
table[1] = ptr = table[0];
for (i = 1; i <= ns_final; i++) {
ht_ptr = hash_rec(&ht, i);
ptr2 = ht_ptr + 1;
ptr2e = ht_ptr + hash_rec_len(&ht, i) - 1;
while (ptr2 <= ptr2e)
*(ptr++) = *(ptr2++);
table[i + 1] = ptr;
}
}
}
hash_clear(&ht);
tfree(block_numa);
tfree(block_numb);
tfree(fsaptr->is_accessible);
if (kbm_print_level >= 3)
printf(" #Number of iterations = %d.\n", num_iterations);
return 0;
}
/* *migmptr should be the general mi-multiplier fsa of a
* cose automatic group.
* This function calculates the transition table of the general mi-multiplier
* for products of two generators. This is output (in unformatted form) to the
* file with name migm2filename, followed by a list of states, which
* specifies which states are accept-states for which length-two words.
* It can then be minimised for any such word as required.
* NOTE - in the mi version readback must be true for the time being.
* prefix is a string which is used as a prefix for the names of the new
* generators, on which the subgroup will be presented.
* They will be called 'prefix'1, 'prefix'2,...
* (as in fsa_mimakemult).
*/
fsa *fsa_migm2(fsa *migmptr, storage_type op_table_type, boolean destroy,
char *migm2filename, boolean readback, char *prefix)
{
if (kbm_print_level >= 3)
printf(" #Calling fsa_migm2.\n");
if (migmptr->states->size < MAXUSHORT)
return fsa_migm2_short(migmptr, op_table_type, destroy, migm2filename,
readback, prefix);
else
return fsa_migm2_int(migmptr, op_table_type, destroy, migm2filename,
readback, prefix);
}
static fsa *fsa_migm2_short(fsa *migmptr, storage_type op_table_type,
boolean destroy, char *migm2filename,
boolean readback, char *prefix)
{
int **table, ne, ngens, ngens1, ns, *fsarow, e, e1, e2, es1, ef1, dr, nt,
cstate, im, csa, csb, csima, csimb, j1, j2, i, j, ct, g1, g2, g3, len = 0,
len1, reclen, ninit_ip, ninit_op, pl, nlabs;
unsigned short *ht_ptr, *ht_chptr, *ht_ptrb, *ht_ptre, *cs_ptr, *cs_ptre,
*ptr;
boolean dense_ip, dense_op, got, leftpad, rightpad;
setToLabelsType *label_ip, *label_op, l1, l2;
gen **labellist1, **labellist2;
short_hash_table ht, labelht;
fsa *migm2ptr;
srec *labs_op, *labs_ip;
FILE *tempfile;
if (kbm_print_level >= 3)
printf(" #Calling fsa_migm2_short.\n");
if (!migmptr->flags[MIDFA]) {
fprintf(stderr, "Error: fsa_migm2 only applies to MIDFA's.\n");
return 0;
}
if (migmptr->alphabet->type != PRODUCT || migmptr->alphabet->arity != 2) {
fprintf(stderr, "Error in fsa_migm2: fsa must be 2-variable.\n");
return 0;
}
if (migmptr->states->type != LABELED) {
fprintf(stderr, "Error in fsa_migm2: states must be labeled.\n");
return 0;
}
tmalloc(migm2ptr, fsa, 1);
fsa_init(migm2ptr);
srec_copy(migm2ptr->alphabet, migmptr->alphabet);
migm2ptr->flags[MIDFA] = TRUE;
migm2ptr->flags[ACCESSIBLE] = TRUE;
migm2ptr->table->table_type = op_table_type;
migm2ptr->table->denserows = 0;
migm2ptr->table->printing_format = op_table_type;
if (!readback) {
tmalloc(migm2ptr->table->filename, char, stringlen(migm2filename) + 1);
strcpy(migm2ptr->table->filename, migm2filename);
}
ne = migmptr->alphabet->size;
ngens = migmptr->alphabet->base->size;
ngens1 = ngens + 1;
if (ne != ngens1 * ngens1 - 1) {
fprintf(stderr, "Error: in a 2-variable fsa, alphabet size should = "
"(ngens+1)^2 - 1.\n");
return 0;
}
fsa_set_is_accepting(migmptr);
dense_ip = migmptr->table->table_type == DENSE;
dr = migmptr->table->denserows;
dense_op = op_table_type == DENSE;
table = migmptr->table->table_data_ptr;
short_hash_init(&ht, FALSE, 0, 0, 0);
/* The initial states of *migm2ptr will be pairs (s1,s2), where s1 and s2 are
* initial states migmptr.
* Their labels will be in terms of the new subgroup generator 'prefix'i to
* be introduced.
* However, their labels may also contain words in the G-generators of length
* 2 (which occur in the accepting states of *migmptr2), so we will not deal
* with the labels until later.
*/
ninit_ip = migmptr->num_initial;
ninit_op = ninit_ip * ninit_ip;
migm2ptr->num_initial = ninit_op;
tmalloc(migm2ptr->initial, int, ninit_op + 1);
for (i = 1; i <= ninit_op; i++)
migm2ptr->initial[i] = i;
ct = 0;
for (i = 1; i <= ninit_ip; i++)
for (j = 1; j <= ninit_ip; j++) {
ct++;
/* Each state in the composite transition table will be represented as a
* subset of the set of ordered pairs in which the components are states
* of *migmptr. The initial states contain just one such pair, of which
* the components are the initial states of *migmptr. The subsets will be
* stored as variable-length records in the hash-table, as a list of pairs
* in increasing order. If a state is reached from a transition ($,x) or
* (x,$) (with $ the padding symbol), then it needs to be marked as such,
* since we do not allow a $ to be followed by any other generator. We do
* this by adding a 1 or a 2 to the end of the statelist - this is
* recognisable by the fact that the length of the statelist then becomes
* odd.
*/
ht_ptr = ht.current_ptr;
ht_ptr[0] = migmptr->initial[i];
ht_ptr[1] = migmptr->initial[j];
im = short_hash_locate(&ht, 2);
if (im != ct) {
fprintf(stderr, "Hash-initialisation problem in fsa_migm2.\n");
return 0;
}
}
if ((tempfile = fopen(migm2filename, "w")) == 0) {
fprintf(stderr, "Error: cannot open file %s\n", migm2filename);
return 0;
}
if (dense_op)
tmalloc(fsarow, int, ne) else tmalloc(fsarow, int, 2 * ne + 1)
cstate = 0;
if (dense_op)
len = ne; /* The length of the fsarow output. */
nt = 0; /* Number of transitions in migm2ptr */
while (++cstate <= ht.num_recs) {
if (kbm_print_level >= 3) {
if ((cstate <= 1000 && cstate % 100 == 0) ||
(cstate <= 10000 && cstate % 1000 == 0) ||
(cstate <= 100000 && cstate % 5000 == 0) || cstate % 50000 == 0)
printf(" #cstate = %d; number of states = %d.\n", cstate,
ht.num_recs);
}
reclen = short_hash_rec_len(&ht, cstate);
cs_ptr = short_hash_rec(&ht, cstate);
cs_ptre = short_hash_rec(&ht, cstate) + reclen - 1;
/* for (i=0;i<reclen;i++) printf(" %d",cs_ptr[i]); printf("\n"); */
if (reclen % 2 == 1) {
if (*cs_ptre == 1) {
leftpad = TRUE;
rightpad = FALSE;
}
else {
leftpad = FALSE;
rightpad = TRUE;
}
cs_ptre--;
}
else {
leftpad = FALSE;
rightpad = FALSE;
}
if (dense_op)
for (i = 0; i < ne; i++)
fsarow[i] = 0;
else
len = 0;
e = 0; /* e is the edge number of generator pair (g1,g3) */
for (g1 = 1; g1 <= ngens1; g1++)
for (g3 = 1; g3 <= ngens1; g3++) {
/* Calculate action of pair (g1,g3) on state cstate - to get the image,
* we have to apply ( (g1,g2), (g2,g3) ) to each ordered pair in the
* subset corresponding to cstate, * and this for each generator g2 of
* the base-alphabet (including the padding symbol).
*/
e++;
if (g1 == ngens1 && g3 == ngens1)
continue;
if ((leftpad && g1 <= ngens) || (rightpad && g3 <= ngens))
continue;
ht_ptrb = ht.current_ptr;
ht_ptre = ht_ptrb - 1;
es1 = (g1 - 1) * ngens1 + 1;
ef1 = g1 * ngens1;
/* As g2 ranges from 1 to ngens+1 in the pair (g1,g2), for fixed g1, the
* corresponding edge number in the fsa ranges from es1 to ef1.
*
* As g2 ranges from 1 to ngens+1 in the pair (g2,g3), for fixed g3, the
* corresponding edge number in the fsa ranges from g3 upwards, with
* increments of size ngens+1.
*/
ptr = cs_ptr;
while (ptr <= cs_ptre) {
csa = *(ptr++);
csb = *(ptr++);
for (g2 = 1, e1 = es1, e2 = g3; e1 <= ef1; g2++, e1++, e2 += ngens1) {
csima = g1 == ngens1 && g2 == ngens1
? (migmptr->is_accepting[csa] ? csa : 0)
: target(dense_ip, table, e1, csa, dr);
if (csima == 0)
continue;
csimb = g2 == ngens1 && g3 == ngens1
? (migmptr->is_accepting[csb] ? csb : 0)
: target(dense_ip, table, e2, csb, dr);
if (csimb == 0)
continue;
if (ht_ptrb > ht_ptre || csima > *(ht_ptre - 1) ||
(csima == *(ht_ptre - 1) && csimb > *ht_ptre)) {
/* We have a new state for the image subset to be added to the end
*/
*(++ht_ptre) = csima;
*(++ht_ptre) = csimb;
}
else {
ht_chptr = ht_ptrb;
while (*ht_chptr < csima)
ht_chptr += 2;
while (*ht_chptr == csima && *(ht_chptr + 1) < csimb)
ht_chptr += 2;
if (csima < *ht_chptr || csimb < *(ht_chptr + 1)) {
/* we have a new state for the image subset to be added in the
* middle */
ht_ptr = ht_ptre;
ht_ptre += 2;
while (ht_ptr >= ht_chptr) {
*(ht_ptr + 2) = *ht_ptr;
ht_ptr--;
}
*ht_chptr = csima;
*(ht_chptr + 1) = csimb;
}
}
}
}
if (ht_ptre > ht_ptrb) {
if (g1 == ngens1)
*(++ht_ptre) = 1;
else if (g3 == ngens1)
*(++ht_ptre) = 2;
}
im = short_hash_locate(&ht, ht_ptre - ht_ptrb + 1);
if (im > 0) {
if (dense_op)
fsarow[e - 1] = im;
else {
fsarow[++len] = e;
fsarow[++len] = im;
}
nt++;
}
}
if (!dense_op)
fsarow[0] = len++;
fwrite((void *)fsarow, sizeof(int), (size_t)len, tempfile);
}
fclose(tempfile);
ns = migm2ptr->states->size = ht.num_recs;
migm2ptr->table->numTransitions = nt;
if (kbm_print_level >= 3)
printf(" #Calculated transitions - %d states, %d transitions.\n", ns,
nt);
/* Locate the accept states Mult_(a*b) for each generator pair (a,b).
* This is slightly cumbersome, since a states
* is an accept state if either the subset representing S contains a
* pair (s1,s2), where s1 is accept state for Mult_a and s2 for Mult_b,
* or we can get from to such a state by applying ( ($,g), (g,$) ) to one
* of the pairs in S, for some generator g.
* It is most convenient to work out this information taking the states
* S in reverse order.
* The information on the accept-states will be stored as labels, which
* will be lists of words in the generators. Each such list will contain
* a1*b1,a2*b2,...,ar*br, where the (ai,bi) are the generator pairs for
* which that particular state is an accept state.
* The identity generator will be numbered ngens+1 and given name '_'
* rather than represented by the empty word. This is so that we can
* distinguish between multipliers for "_*a" and "a*_" if necessary.
* HOWEVER, the lists for the initial states will also contain a term
* 'prefix'i*'prefix'j (a product of two of the new subgroup generators).
* The label of the first initial state of *migmptr is always the empty
* word - so the label of initial state i+1 of *migmptr for i>0 will be
* correspond to the new generator 'prefix'i.
* The labels will be collected initially in a new hash-table.
*/
labs_ip = migmptr->states->labels;
if (labs_ip->alphabet_size != ngens) {
fprintf(stderr, "Error in fsa_migm2: state label set has wrong size!\n");
return 0;
}
migm2ptr->states->type = LABELED;
tmalloc(migm2ptr->states->labels, srec, 1);
labs_op = migm2ptr->states->labels;
labs_op->type = LISTOFWORDS;
/* The alphabet for the labels for *migm2ptr will contain the old
* generators (1 to ngens), the empty word ngens = ('_') and the new
* subgroup generators (ngens+2 to ngens+ninit_ip), with names
* 'prefix'1 to 'prefix'(ninit_ip-1).
*/
labs_op->alphabet_size = ngens + ninit_ip;
for (i = 1; i <= ngens; i++) {
tmalloc(labs_op->alphabet[i], char, stringlen(labs_ip->alphabet[i]) + 1);
strcpy(labs_op->alphabet[i], labs_ip->alphabet[i]);
}
tmalloc(labs_op->alphabet[ngens1], char, 2);
strcpy(labs_op->alphabet[ngens1], "_");
if (labs_ip->wordslist[migmptr->initial[1]][0][0] != 0) {
fprintf(stderr,
"Error in fsa_migm2 - first initial state not empty word.\n");
return 0;
}
pl = stringlen(prefix);
for (i = 2; i <= ninit_ip; i++) {
tmalloc(labs_op->alphabet[ngens + i], char, pl + int_len(i - 1) + 1);
sprintf(labs_op->alphabet[ngens + i], "%s%d", prefix, i - 1);
}
label_ip = migmptr->states->setToLabels;
tmalloc(migm2ptr->states->setToLabels, setToLabelsType, ns + 1);
label_op = migm2ptr->states->setToLabels;
tmalloc(migm2ptr->is_accepting, boolean, ns + 1);
for (i = 1; i <= ns; i++)
migm2ptr->is_accepting[i] = FALSE;
short_hash_init(&labelht, FALSE, 0, LABHTSIZE, 2 * LABHTSIZE);
es1 = ngens * ngens1 + 1;
ef1 = ngens1 * ngens1 - 1;
for (cstate = ns; cstate >= 1; cstate--) {
/* We work backwards through the states, since we wish to add on additional
* elements at the end of the list in the hash-table - this destroys
* later entries, but that doesn't matter, since we have already dealt
* with them.
*/
cs_ptr = short_hash_rec(&ht, cstate);
reclen = short_hash_rec_len(&ht, cstate);
if (reclen % 2 == 1)
reclen--; /* The last entry marks the fact that this is a "padded state"*/
cs_ptre = short_hash_rec(&ht, cstate) + reclen - 1;
/* Apply generators ( ($,g2), (g2,$) ) and see if we get anything new.
* We won't bother about having them in increasing order this time.
*/
ptr = cs_ptr;
while (ptr <= cs_ptre) {
csa = *(ptr++);
csb = *(ptr++);
for (e1 = es1, e2 = ngens1; e1 <= ef1; e1++, e2 += ngens1) {
csima = target(dense_ip, table, e1, csa, dr);
if (csima == 0)
continue;
csimb = target(dense_ip, table, e2, csb, dr);
if (csimb == 0)
continue;
/* See if (csima,csimb) is new */
ht_chptr = cs_ptr;
got = FALSE;
while (ht_chptr < cs_ptre) {
if (csima == ht_chptr[0] && csimb == ht_chptr[1]) {
got = TRUE;
break;
}
ht_chptr += 2;
}
if (!got) {
/* add (csima,csimb) to the end */
*(++cs_ptre) = csima;
*(++cs_ptre) = csimb;
}
}
}
/* Now we see which pairs in the subset are of form (s,t), where s is
* an accept state for a generator a, and t for a generator b.
* The list of all such pairs (a,b) will form the label of the state, which
* will be start with the list of words [a1*b1,a2*b2,..,ar*br], with the
* (ai,bi) coming in lex-order.
*/
ht_ptrb = labelht.current_ptr;
ht_ptre = ht_ptrb - 1;
ptr = cs_ptr;
while (ptr <= cs_ptre) {
csa = *(ptr++);
csb = *(ptr++);
if (((l1 = label_ip[csa]) != 0) && ((l2 = label_ip[csb]) != 0)) {
labellist1 = migmptr->states->labels->wordslist[l1];
labellist2 = migmptr->states->labels->wordslist[l2];
j1 = 0;
while (labellist1[j1]) {
if (genstrlen(labellist1[j1]) > 1) {
j1++;
continue;
}
g1 = labellist1[j1][0];
if (g1 == 0)
g1 = ngens1;
j2 = 0;
while (labellist2[j2]) {
if (genstrlen(labellist2[j2]) > 1) {
j2++;
continue;
}
migm2ptr->is_accepting[cstate] = TRUE;
g2 = labellist2[j2][0];
if (g2 == 0)
g2 = ngens1;
if (ht_ptrb > ht_ptre || g1 > *(ht_ptre - 1) ||
(g1 == *(ht_ptre - 1) && g2 > *ht_ptre)) {
/* We have a new generator pair to be added to the end */
*(++ht_ptre) = g1;
*(++ht_ptre) = g2;
}
else {
ht_chptr = ht_ptrb;
while (*ht_chptr < g1)
ht_chptr += 2;
while (*ht_chptr == g1 && *(ht_chptr + 1) < g2)
ht_chptr += 2;
if (g1 < *ht_chptr || g2 < *(ht_chptr + 1)) {
/* we have a new generator pair to be added in the middle */
ht_ptr = ht_ptre;
ht_ptre += 2;
while (ht_ptr >= ht_chptr) {
*(ht_ptr + 2) = *ht_ptr;
ht_ptr--;
}
*ht_chptr = g1;
*(ht_chptr + 1) = g2;
}
}
j2++;
}
j1++;
}
}
}
if (cstate <= ninit_op && cstate > 1) {
/* cstate is an initial state.
* We have an extra term to add to the label-list, for the product of
* the two new subgroup generators
*/
g1 = (cstate - 1) / ninit_ip;
g2 = (cstate - 1) % ninit_ip;
if (g1 == 0)
*(++ht_ptre) = ngens1 + g2;
else if (g2 == 0)
*(++ht_ptre) = ngens1 + g1;
else {
*(++ht_ptre) = ngens1 + g1;
*(++ht_ptre) = ngens1 + g2;
}
}
/* That completes the calculation of the label for cstate */
label_op[cstate] = short_hash_locate(&labelht, ht_ptre - ht_ptrb + 1);
}
short_hash_clear(&ht);
ct = 0;
for (i = 1; i <= ns; i++)
if (migm2ptr->is_accepting[i])
ct++;
migm2ptr->num_accepting = ct;
if (ct == 1 || ct != ns) {
tmalloc(migm2ptr->accepting, int, ct + 1);
ct = 0;
for (i = 1; i <= ns; i++)
if (migm2ptr->is_accepting[i])
migm2ptr->accepting[++ct] = i;
}
tfree(migm2ptr->is_accepting);
tfree(migmptr->is_accepting);
tfree(fsarow);
if (destroy)
fsa_clear(migmptr);
/* Finally copy the records from the label hash-table into the set of labels
*/
nlabs = labs_op->size = labelht.num_recs;
if (kbm_print_level >= 3)
printf(" #There are %d distinct labels.\n", nlabs);
tmalloc(labs_op->wordslist, gen **, nlabs + 1);
for (i = 1; i <= nlabs; i++) {
len = short_hash_rec_len(&labelht, i) / 2;
len1 = (short_hash_rec_len(&labelht, i) + 1) / 2;
tmalloc(labs_op->wordslist[i], gen *, len1 + 1);
ht_ptr = short_hash_rec(&labelht, i);
for (j = 0; j < len; j++) {
tmalloc(labs_op->wordslist[i][j], gen, 3);
labs_op->wordslist[i][j][0] = ht_ptr[2 * j];
labs_op->wordslist[i][j][1] = ht_ptr[2 * j + 1];
labs_op->wordslist[i][j][2] = 0;
}
if (len < len1) {
tmalloc(labs_op->wordslist[i][len], gen, 2);
labs_op->wordslist[i][j][0] = ht_ptr[2 * len];
labs_op->wordslist[i][j][1] = 0;
}
labs_op->wordslist[i][len1] = 0;
}
short_hash_clear(&labelht);
if (readback) {
tempfile = fopen(migm2filename, "r");
compressed_transitions_read(migm2ptr, tempfile);
fclose(tempfile);
unlink(migm2filename);
}
return migm2ptr;
}
static fsa *fsa_migm2_int(fsa *migmptr, storage_type op_table_type,
boolean destroy, char *migm2filename,
boolean readback, char *prefix)
{
fprintf(stderr, "Sorry - fsa_migm2 is not yet implemented for machines.\n");
fprintf(stderr, "with more than 65536 states.\n");
return 0;
}
/* This procedure takes the fsa *migmptr produced by fsa_mitriples,
* and builds a particular multiple-initial state multiplier Mult_gi.
* This merely involves setting the accept states of *genmimultptr
* in accordance with the labels of its states.
* The labels for the initial states (which will become the subgroup
* generators) are renamed prefix'1', prefix'2', ..., where prefix is
* the string argument supplied.
* g can be 0, for the identity generator, which (in shortlex case) inevitably
* produces the diagonal of the word-acceptor.
* This procedure alters its arguments and does not return anything.
*/
int fsa_mimakemult(fsa *migmptr, int g, char *prefix)
{
int ngens, ns, ni, i, j, ct, pl;
gen **labellist;
setToLabelsType *labno;
srec *labs;
if (kbm_print_level >= 3)
printf(" #Calling fsa_mimakemult with generator number %d.\n", g);
if (!migmptr->flags[MIDFA]) {
fprintf(stderr, "Error: fsa_mimakemult only applies to MIDFA's.\n");
return -1;
}
if (migmptr->alphabet->type != PRODUCT || migmptr->alphabet->arity != 2) {
fprintf(stderr, "Error in fsa_mimakemult: fsa must be 2-variable.\n");
return -1;
}
if (migmptr->states->type != LABELED) {
fprintf(stderr,
"Error in fsa_mimakemult: states of fsa must be labeled.\n");
return -1;
}
ns = migmptr->states->size;
ngens = migmptr->alphabet->base->size;
if (g < 0 || g > ngens + 1) {
fprintf(stderr, "#Error in fsa_mimakemult: Generator is out of range.\n");
return -1;
}
tfree(migmptr->accepting);
labno = migmptr->states->setToLabels;
labs = migmptr->states->labels;
ct = 0;
for (i = 1; i <= ns; i++)
if (labno[i] > 0) {
labellist = labs->wordslist[labno[i]];
j = 0;
while (labellist[j]) {
if (labellist[j][0] == g && (g == 0 || labellist[j][1] == 0))
ct++;
j++;
}
}
migmptr->num_accepting = ct;
if (ct < ns || ns == 1) {
tmalloc(migmptr->accepting, int, ct + 1);
ct = 0;
for (i = 1; i <= ns; i++)
if (labno[i] > 0) {
labellist = labs->wordslist[labno[i]];
j = 0;
while (labellist[j]) {
if (labellist[j][0] == g && (g == 0 || labellist[j][1] == 0))
migmptr->accepting[++ct] = i;
j++;
}
}
}
/* Finally we relabel the states - only the initial states will now have
* labels First check that the first initial state has label the empty word -
* otherwise something has gone badly wrong!
*/
if (labs->wordslist[migmptr->initial[1]][0][0] != 0) {
fprintf(stderr,
"Error in fsa_mimakemult - first initial state not empty word.\n");
return -1;
}
srec_clear(labs);
ni = migmptr->num_initial;
labs->size = ni;
labs->alphabet_size = ni - 1;
tmalloc(labs->wordslist, gen **, ni + 1);
pl = stringlen(prefix);
for (i = 1; i <= ns; i++)
labno[i] = 0;
/* The first initial state has label the empty word */
tmalloc(labs->wordslist[1], gen *, 2);
tmalloc(labs->wordslist[1][0], gen, 1);
labs->wordslist[1][0][0] = 0;
labs->wordslist[1][1] = 0;
labno[migmptr->initial[1]] = 1;
for (i = 2; i <= ni; i++) {
tmalloc(labs->wordslist[i], gen *, 2);
tmalloc(labs->wordslist[i][0], gen, 2);
tmalloc(labs->alphabet[i - 1], char, pl + int_len(i - 1) + 1);
labs->wordslist[i][0][0] = i - 1;
labs->wordslist[i][0][1] = 0;
sprintf(labs->alphabet[i - 1], "%s%d", prefix, i - 1);
labs->wordslist[i][1] = 0;
labno[migmptr->initial[i]] = i;
}
return 0;
}
/* This procedure takes the fsa *migm2ptr produced by fsa_migenmult2,
* and builds a particular length-2 multiplier Mult_g1g2.
* This merely involves locating the accept states.
* This procedure alters its arguments and does not return anything.
* prefix is the prefix of the names of the subgroup generators to be used -
* For a particular group, this MUST be the same throughout for all calls of
* fsa_migm2, fsa_mimakemult and fsa_mimakemult2.
*/
int fsa_mimakemult2(fsa *migm2ptr, int g1, int g2, char *prefix)
{
int ngens, ns, nlabs, i, j, k, ct, len = 0, ni, preflen, shift, alphsize;
gen ***labellist, ***newlabellist, **oldlist;
char **alph;
boolean *accept, found;
setToLabelsType *labelnumber;
if (kbm_print_level >= 3)
printf(" #Calling fsa_mimakemult2 with generators %d and %d.\n", g1, g2);
if (!migm2ptr->flags[MIDFA]) {
fprintf(stderr, "Error: fsa_mimakemult2 only applies to DFA's.\n");
return -1;
}
if (migm2ptr->alphabet->type != PRODUCT || migm2ptr->alphabet->arity != 2) {
fprintf(stderr, "Error in fsa_mimakemult2: fsa must be 2-variable.\n");
return -1;
}
if (migm2ptr->states->type != LABELED) {
fprintf(stderr,
"Error in fsa_mimakemult2: states of fsa must be labeled.\n");
return -1;
}
if (migm2ptr->states->labels->type != LISTOFWORDS) {
fprintf(stderr,
"Error in fsa_mimakemult2: labels must be lists of words.\n");
return -1;
}
ns = migm2ptr->states->size;
nlabs = migm2ptr->states->labels->size;
ngens = migm2ptr->alphabet->base->size + 1;
if (g1 <= 0 || g1 > ngens || g2 <= 0 || g2 > ngens) {
fprintf(stderr, "#Error in fsa_makemult2: A generator is out of range.\n");
return -1;
}
tfree(migm2ptr->accepting);
tmalloc(accept, boolean, nlabs + 1);
labellist = migm2ptr->states->labels->wordslist;
/* Now we see which labels are accept-labels for the pair (g1,g2) - the
* label is an accept-label if the list of words which is its name
* contains g1*g2.
*/
accept[0] = FALSE;
for (i = 1; i <= nlabs; i++) {
accept[i] = FALSE;
j = 0;
while (labellist[i][j]) {
if (labellist[i][j][0] == g1 && labellist[i][j][1] == g2) {
accept[i] = TRUE;
break;
}
j++;
}
}
/* Now we can see which states are accept-states. A state is an accept-state
* iff its label is an accept-label.
* First we count the number.
*/
ct = 0;
labelnumber = migm2ptr->states->setToLabels;
for (i = 1; i <= ns; i++)
if (accept[labelnumber[i]])
ct++;
migm2ptr->num_accepting = ct;
if (ct < ns || ns == 1) {
tfree(migm2ptr->accepting);
tmalloc(migm2ptr->accepting, int, ct + 1);
ct = 0;
for (i = 1; i <= ns; i++)
if (accept[labelnumber[i]])
migm2ptr->accepting[++ct] = i;
}
tfree(accept);
/* Finally we need to relabel the initial states - each such state will
* have its own label, consisting of a single word of length at most 2
* in the subgroup generators - we find it by looking in the existing
* label for that state.
* The alphabet for the labels will also need to be changed, since the
* first few terms in it are group-generators and are not needed any more.
* We do that first.
*/
preflen = stringlen(prefix);
alph = migm2ptr->states->labels->alphabet;
alphsize = migm2ptr->states->labels->alphabet_size;
shift = 0;
for (i = 1; i <= alphsize; i++) {
if (strncmp(prefix, alph[i], preflen) == 0) {
shift = i - 1;
/* shift is the number of terms of the alphabet that we will discard */
break;
}
}
if (i > alphsize) {
fprintf(stderr, "Error in fsamimakemult2 - subgroup prefix wrong?\n");
return -1;
}
for (i = 1; i <= alphsize; i++) {
if (i > shift) {
tmalloc(alph[i - shift], char, stringlen(alph[i]) + 1);
strcpy(alph[i - shift], alph[i]);
}
tfree(alph[i]);
}
alphsize -= shift;
migm2ptr->states->labels->alphabet_size = alphsize;
ni = migm2ptr->num_initial;
tmalloc(newlabellist, gen **, ni + 1);
for (i = 1; i <= ni; i++) {
tmalloc(newlabellist[i], gen *, 2);
oldlist = labellist[labelnumber[migm2ptr->initial[i]]];
j = 0;
found = FALSE;
while (oldlist[j]) {
/* see if this name starts with the subgroup generator prefix */
if (oldlist[j][0] > shift) {
len = genstrlen(oldlist[j]);
tmalloc(newlabellist[i][0], gen, len + 1);
genstrcpy(newlabellist[i][0], oldlist[j]);
for (k = 0; k < len; k++)
newlabellist[i][0][k] -= shift;
found = TRUE;
break;
}
j++;
}
if (!found) {
/* label is empty word */
tmalloc(newlabellist[i][0], gen, 1);
newlabellist[i][0][0] = 0;
}
newlabellist[i][1] = 0;
labelnumber[migm2ptr->initial[i]] = i;
}
for (i = 1; i <= nlabs; i++) {
oldlist = labellist[i];
j = 0;
while (oldlist[j]) {
tfree(oldlist[j]);
j++;
}
tfree(oldlist);
}
tfree(migm2ptr->states->labels->wordslist);
migm2ptr->states->labels->size = ni;
migm2ptr->states->labels->wordslist = newlabellist;
return 0;
}
/* *mult1ptr and *mult2ptr should be multiplier fsa's of an automatic group.
* This function calculates the composite of these two multipliers.
* So if *mult1ptr is the mutiplier for the word w1 and *mult2ptr for w2,
* then the returned fsa is the multiplier for w1*w2.
*/
fsa *fsa_micomposite(fsa *mult1ptr, fsa *mult2ptr, storage_type op_table_type,
boolean destroy, char *compfilename, boolean readback)
{
if (kbm_print_level >= 3)
printf(" #Calling fsa_micomposite.\n");
if (mult1ptr->states->size < MAXUSHORT && mult2ptr->states->size < MAXUSHORT)
return fsa_micomposite_short(mult1ptr, mult2ptr, op_table_type, destroy,
compfilename, readback);
else
return fsa_micomposite_int(mult1ptr, mult2ptr, op_table_type, destroy,
compfilename, readback);
}
static fsa *fsa_micomposite_short(fsa *mult1ptr, fsa *mult2ptr,
storage_type op_table_type, boolean destroy,
char *compfilename, boolean readback)
{
int **table1, **table2, ne, ngens, ngens1, ns, *fsarow, e, e1, e2, es1, ef1,
dr1, dr2, nt, cstate, im, csa, csb, csima, csimb, i, j, l1, l2, ct, g1,
g2, g3, len = 0, reclen, ni1, ni2, ni;
unsigned short *ht_ptr, *ht_chptr, *ht_ptrb, *ht_ptre, *cs_ptr, *cs_ptre,
*ptr;
boolean dense_ip1, dense_ip2, dense_op, got, leftpad, rightpad;
short_hash_table ht;
fsa *micompositeptr;
srec *labs, *labs1, *labs2;
FILE *tempfile;
if (kbm_print_level >= 3)
printf(" #Calling fsa_micomposite_short.\n");
if (!mult1ptr->flags[MIDFA] || !mult2ptr->flags[MIDFA]) {
fprintf(stderr, "Error: fsa_micomposite only applies to MIDFA's.\n");
return 0;
}
if (mult1ptr->alphabet->type != PRODUCT || mult1ptr->alphabet->arity != 2) {
fprintf(stderr, "Error in fsa_micomposite: fsa must be 2-variable.\n");
return 0;
}
if (mult2ptr->alphabet->type != PRODUCT || mult2ptr->alphabet->arity != 2) {
fprintf(stderr, "Error in fsa_micomposite: fsa must be 2-variable.\n");
return 0;
}
if (!srec_equal(mult1ptr->alphabet, mult2ptr->alphabet)) {
fprintf(stderr,
"Error in fsa_micomposite: fsa's must have same alphabet.\n");
return 0;
}
if (mult1ptr->states->type != LABELED) {
fprintf(stderr, "Error in fsa_micomposite: states must be labeled.\n");
return 0;
}
if (mult2ptr->states->type != LABELED) {
fprintf(stderr, "Error in fsa_micomposite: states must be labeled.\n");
return 0;
}
tmalloc(micompositeptr, fsa, 1);
fsa_init(micompositeptr);
srec_copy(micompositeptr->alphabet, mult1ptr->alphabet);
micompositeptr->states->type = LABELED;
micompositeptr->flags[MIDFA] = TRUE;
micompositeptr->flags[ACCESSIBLE] = TRUE;
micompositeptr->table->table_type = op_table_type;
micompositeptr->table->denserows = 0;
micompositeptr->table->printing_format = op_table_type;
if (!readback) {
tmalloc(micompositeptr->table->filename, char, stringlen(compfilename) + 1);
strcpy(micompositeptr->table->filename, compfilename);
}
ne = mult1ptr->alphabet->size;
ngens = mult1ptr->alphabet->base->size;
ngens1 = ngens + 1;
if (ne != ngens1 * ngens1 - 1) {
fprintf(stderr, "Error: in a 2-variable fsa, alphabet size should = "
"(ngens+1)^2 - 1.\n");
return 0;
}
fsa_set_is_accepting(mult1ptr);
fsa_set_is_accepting(mult2ptr);
dense_ip1 = mult1ptr->table->table_type == DENSE;
dr1 = mult1ptr->table->denserows;
dense_ip2 = mult2ptr->table->table_type == DENSE;
dr2 = mult2ptr->table->denserows;
dense_op = op_table_type == DENSE;
table1 = mult1ptr->table->table_data_ptr;
table2 = mult2ptr->table->table_data_ptr;
short_hash_init(&ht, FALSE, 0, 0, 0);
/* Sort out initial states of *micompositeptr -
* these will be pairs (s1,s2), where s1 and s2 are initial states
* of *multptr1 and *multptr2, respectively.
* Their labels will be the products of the corresponding labels of s1,s2.
*/
ni1 = mult1ptr->num_initial;
ni2 = mult2ptr->num_initial;
ni = ni1 * ni2;
micompositeptr->num_initial = ni;
tmalloc(micompositeptr->initial, int, ni + 1);
for (i = 1; i <= ni; i++)
micompositeptr->initial[i] = i;
tmalloc(micompositeptr->states->labels, srec, 1);
labs = micompositeptr->states->labels;
labs1 = mult1ptr->states->labels;
labs2 = mult2ptr->states->labels;
labs->size = ni;
labs->type = LISTOFWORDS;
labs->alphabet_size = labs1->alphabet_size;
for (i = 1; i <= labs->alphabet_size; i++) {
tmalloc(labs->alphabet[i], char, stringlen(labs1->alphabet[i]) + 1);
strcpy(labs->alphabet[i], labs1->alphabet[i]);
}
tmalloc(labs->wordslist, gen **, ni + 1);
ct = 0;
for (i = 1; i <= ni1; i++)
for (j = 1; j <= ni2; j++) {
ct++;
tmalloc(labs->wordslist[ct], gen *, 2);
l1 = genstrlen(labs1->wordslist[i][0]);
l2 = genstrlen(labs2->wordslist[j][0]);
tmalloc(labs->wordslist[ct][0], gen, l1 * l2 + 1);
genstrcpy(labs->wordslist[ct][0], labs1->wordslist[i][0]);
genstrcat(labs->wordslist[ct][0], labs2->wordslist[j][0]);
labs->wordslist[ct][1] = 0;
/* Each state in the composite transition table will be represented as a
* subset of the set of ordered pairs in which the first component is a
* state in *multptr1 and the second a state in *multptr2. The initial
* states contain just one such pair, of which the components are the
* initial states of *mult1ptr and *multptr2. The subsets will be stored
* as variable-length records in the hash-table, as a list of pairs in
* increasing order. If a state is reached from a transition ($,x) or
* (x,$) (with $ the padding symbol), then it needs to be marked as such,
* since we do not allow a $ to be followed by any other generator. We do
* this by adding a 1 or a 2 to the end of the statelist - this is
* recognisable by the fact that the length of the statelist then becomes
* odd.
*/
ht_ptr = ht.current_ptr;
ht_ptr[0] = mult1ptr->initial[i];
ht_ptr[1] = mult2ptr->initial[j];
im = short_hash_locate(&ht, 2);
if (im != ct) {
fprintf(stderr, "Hash-initialisation problem in fsa_micomposite.\n");
return 0;
}
}
if ((tempfile = fopen(compfilename, "w")) == 0) {
fprintf(stderr, "Error: cannot open file %s\n", compfilename);
return 0;
}
if (dense_op)
tmalloc(fsarow, int, ne) else tmalloc(fsarow, int, 2 * ne + 1)
cstate = 0;
if (dense_op)
len = ne; /* The length of the fsarow output. */
nt = 0; /* Number of transitions in micompositeptr */
while (++cstate <= ht.num_recs) {
if (kbm_print_level >= 3) {
if ((cstate <= 1000 && cstate % 100 == 0) ||
(cstate <= 10000 && cstate % 1000 == 0) ||
(cstate <= 100000 && cstate % 5000 == 0) || cstate % 50000 == 0)
printf(" #cstate = %d; number of states = %d.\n", cstate,
ht.num_recs);
}
reclen = short_hash_rec_len(&ht, cstate);
cs_ptr = short_hash_rec(&ht, cstate);
cs_ptre = short_hash_rec(&ht, cstate) + reclen - 1;
/* for (i=0;i<reclen;i++) printf(" %d",cs_ptr[i]); printf("\n"); */
if (reclen % 2 == 1) {
if (*cs_ptre == 1) {
leftpad = TRUE;
rightpad = FALSE;
}
else {
leftpad = FALSE;
rightpad = TRUE;
}
cs_ptre--;
}
else {
leftpad = FALSE;
rightpad = FALSE;
}
if (dense_op)
for (i = 0; i < ne; i++)
fsarow[i] = 0;
else
len = 0;
e = 0; /* e is the edge number of generator pair (g1,g3) */
for (g1 = 1; g1 <= ngens1; g1++)
for (g3 = 1; g3 <= ngens1; g3++) {
/* Calculate action of pair (g1,g3) on state cstate - to get the image,
* we have to apply ( (g1,g2), (g2,g3) ) to each ordered pair in the
* subset corresponding to cstate, * and this for each generator g2 of
* the base-alphabet (including the padding symbol).
*/
e++;
if (g1 == ngens1 && g3 == ngens1)
continue;
if ((leftpad && g1 <= ngens) || (rightpad && g3 <= ngens))
continue;
ht_ptrb = ht.current_ptr;
ht_ptre = ht_ptrb - 1;
es1 = (g1 - 1) * ngens1 + 1;
ef1 = g1 * ngens1;
/* As g2 ranges from 1 to ngens+1 in the pair (g1,g2), for fixed g1, the
* corresponding edge number in the fsa ranges from es1 to ef1.
*
* As g2 ranges from 1 to ngens+1 in the pair (g2,g3), for fixed g3, the
* corresponding edge number in the fsa ranges from g3 upwards, with
* increments of size ngens+1.
*/
ptr = cs_ptr;
while (ptr <= cs_ptre) {
csa = *(ptr++);
csb = *(ptr++);
for (g2 = 1, e1 = es1, e2 = g3; e1 <= ef1; g2++, e1++, e2 += ngens1) {
csima = g1 == ngens1 && g2 == ngens1
? (mult1ptr->is_accepting[csa] > 0 ? csa : 0)
: target(dense_ip1, table1, e1, csa, dr1);
if (csima == 0)
continue;
csimb = g2 == ngens1 && g3 == ngens1
? (mult2ptr->is_accepting[csb] > 0 ? csb : 0)
: target(dense_ip2, table2, e2, csb, dr2);
if (csimb == 0)
continue;
if (ht_ptrb > ht_ptre || csima > *(ht_ptre - 1) ||
(csima == *(ht_ptre - 1) && csimb > *ht_ptre)) {
/* We have a new state for the image subset to be added to the end
*/
*(++ht_ptre) = csima;
*(++ht_ptre) = csimb;
}
else {
ht_chptr = ht_ptrb;
while (*ht_chptr < csima)
ht_chptr += 2;
while (*ht_chptr == csima && *(ht_chptr + 1) < csimb)
ht_chptr += 2;
if (csima < *ht_chptr || csimb < *(ht_chptr + 1)) {
/* we have a new state for the image subset to be added in the
* middle */
ht_ptr = ht_ptre;
ht_ptre += 2;
while (ht_ptr >= ht_chptr) {
*(ht_ptr + 2) = *ht_ptr;
ht_ptr--;
}
*ht_chptr = csima;
*(ht_chptr + 1) = csimb;
}
}
}
}
if (ht_ptre > ht_ptrb) {
if (g1 == ngens1)
*(++ht_ptre) = 1;
else if (g3 == ngens1)
*(++ht_ptre) = 2;
}
im = short_hash_locate(&ht, ht_ptre - ht_ptrb + 1);
if (im > 0) {
if (dense_op)
fsarow[e - 1] = im;
else {
fsarow[++len] = e;
fsarow[++len] = im;
}
nt++;
}
}
if (!dense_op)
fsarow[0] = len++;
fwrite((void *)fsarow, sizeof(int), (size_t)len, tempfile);
}
fclose(tempfile);
ns = micompositeptr->states->size = ht.num_recs;
micompositeptr->table->numTransitions = nt;
if (kbm_print_level >= 3)
printf(" #Calculated transitions - %d states, %d transitions.\n", ns,
nt);
/* Locate the accept states. This is slightly cumbersome, since a state
* is an accept state if either the corresponding subset contains a
* a pair (s1,s2), where s1 is and accept state of *mult1ptr and s2 an
* accept state of *mult2ptr, or we can get from some such state to an
* accept state by applying elements ( ($,x), (x,$ ).
* We will need to use the array micompositeptr->is_accepting.
*/
tmalloc(micompositeptr->is_accepting, boolean, ns + 1);
for (i = 1; i <= ns; i++)
micompositeptr->is_accepting[i] = FALSE;
ct = 0;
es1 = ngens * ngens1 + 1;
ef1 = ngens1 * ngens1 - 1;
for (cstate = ns; cstate >= 1; cstate--) {
/* We work backwards through the states, since we wish to add on additional
* elements at the end of the list in the hash-table - this destroys
* later entries, but that doesn't matter, since we have already dealt
* with them.
*/
cs_ptr = short_hash_rec(&ht, cstate);
reclen = short_hash_rec_len(&ht, cstate);
if (reclen % 2 == 1)
reclen--; /* The last entry marks the fact that this is a "padded state"*/
cs_ptre = short_hash_rec(&ht, cstate) + reclen - 1;
/* First see if the set itself contains an accept-state */
ptr = cs_ptr;
while (ptr <= cs_ptre) {
csa = *(ptr++);
csb = *(ptr++);
if (mult1ptr->is_accepting[csa] && mult2ptr->is_accepting[csb]) {
micompositeptr->is_accepting[cstate] = TRUE;
ct++;
break;
}
}
if (micompositeptr->is_accepting[cstate])
continue;
/* Next apply generators ( ($,g2), (g2,$) ) and see if we get anything new.
* We won't bother about having them in increasing order this time.
*/
ptr = cs_ptr;
while (ptr <= cs_ptre) {
csa = *(ptr++);
csb = *(ptr++);
for (e1 = es1, e2 = ngens1; e1 <= ef1; e1++, e2 += ngens1) {
csima = target(dense_ip1, table1, e1, csa, dr1);
if (csima == 0)
continue;
csimb = target(dense_ip2, table2, e2, csb, dr2);
if (csimb == 0)
continue;
/* see if (csima,csimb) is accepting */
if (mult1ptr->is_accepting[csima] && mult2ptr->is_accepting[csimb]) {
micompositeptr->is_accepting[cstate] = TRUE;
ct++;
break;
}
/* now see if it is new */
ht_chptr = cs_ptr;
got = FALSE;
while (ht_chptr < cs_ptre) {
if (csima == ht_chptr[0] && csimb == ht_chptr[1]) {
got = TRUE;
break;
}
ht_chptr += 2;
}
if (!got) {
/* add (csima,csimb) to the end */
*(++cs_ptre) = csima;
*(++cs_ptre) = csimb;
}
}
if (micompositeptr->is_accepting[cstate])
continue;
}
}
micompositeptr->num_accepting = ct;
if (ct == 1 || ct != ns) {
tmalloc(micompositeptr->accepting, int, ct + 1);
ct = 0;
for (i = 1; i <= ns; i++)
if (micompositeptr->is_accepting[i])
micompositeptr->accepting[++ct] = i;
}
tfree(micompositeptr->is_accepting);
tfree(mult1ptr->is_accepting);
tfree(mult2ptr->is_accepting);
short_hash_clear(&ht);
tfree(fsarow);
if (destroy) {
fsa_clear(mult1ptr);
fsa_clear(mult2ptr);
}
/* Set the setToLabels field of micompositeptr->states */
tmalloc(micompositeptr->states->setToLabels, setToLabelsType, ns + 1);
for (i = 1; i <= ni; i++)
micompositeptr->states->setToLabels[i] = i;
for (i = ni + 1; i <= ns; i++)
micompositeptr->states->setToLabels[i] = 0;
if (readback) {
tempfile = fopen(compfilename, "r");
compressed_transitions_read(micompositeptr, tempfile);
fclose(tempfile);
unlink(compfilename);
}
return micompositeptr;
}
static fsa *fsa_micomposite_int(fsa *mult1ptr, fsa *mult2ptr,
storage_type op_table_type, boolean destroy,
char *compfilename, boolean readback)
{
fprintf(stderr,
"Sorry - fsa_micomposite is not yet implemented for machines.\n");
fprintf(stderr, "with more than 65536 states.\n");
return 0;
}
¤ Dauer der Verarbeitung: 0.35 Sekunden
¤
*© Formatika GbR, Deutschland
|
|