www.pudn.com > bayes.rar > attset2.c
/*----------------------------------------------------------------------
File : attset2.c
Contents: attribute set management, read/write and parse functions
Author : Christian Borgelt
History : 26.10.1995 file created
03.11.1995 functions as_read and as_write added
25.11.1995 functions as_write extended
29.11.1995 output restricted to maxlen characters per line
22.02.1996 functions as_read and as_write redesigned
23.02.1996 function as_desc added
24.02.1996 functions as_save and as_load added
26.02.1997 default attribute name generation added
01.08.1997 restricted read/write/describe added
02.08.1997 additional information output added
26.09.1997 error code added to as_err structure
04.01.1998 scan functions moved to module 'tfscan'
06.01.1998 read/write functions made optional
10.01.1998 variable unknown value characters added
06.03.1998 alignment of last field removed
13.03.1998 adding values from function as_read simplified
23.08.1998 attribute creation and deletion functions added
01.09.1998 several assertions added
06.09.1998 second major redesign completed
14.09.1998 attribute selection in as_write/as_desc improved
17.09.1998 attribute selection improvements completed
04.02.1999 long int changed to int
12.02.1999 attribute reading in mode AS_DFLT improved
23.03.1999 attribute name existence check made optional
03.02.2000 error reporting bug in function as_read fixed
22.11.2000 functions sc_form and sc_len exported
13.05.2001 acceptance of unknown values made optional
23.06.2001 module split into two files
04.10.2001 bug in value range reading removed
22.01.2002 parser functions moved to a separate file
31.05.2002 bug in field reporting in as_read fixed
18.06.2002 check for valid intervals added to as_desc
22.07.2003 bug in as_write (output of UV_FLT) fixed
21.05.2004 semantics of unknown value reading changed
17.01.2006 bug in function as_write fixed (RDORD vs. RANGE)
06.10.2006 adapted to improved function tfs_getfld
----------------------------------------------------------------------*/
#include
#include
#include
#include
#include
#include "attset.h"
#ifdef STORAGE
#include "storage.h"
#endif
/*----------------------------------------------------------------------
Preprocessor Definitions
----------------------------------------------------------------------*/
#define BLKSIZE 16 /* block size for vectors */
/*----------------------------------------------------------------------
Auxiliary Functions
----------------------------------------------------------------------*/
#ifdef AS_RDWR
static int _isunk (TFSCAN *tfscan, const char *s)
{ /* --- test for an unknown value */
assert(tfscan && s); /* check arguments */
while (*s) /* while not at end of string */
if (!tfs_istype(tfscan, TFS_OTHER, *s++))
return 0; /* if a character is not marked, */
return 1; /* return 'not an unknown value', */
} /* _isunk() */ /* otherwise return 'unknown value' */
#endif /* #ifdef AS_RDWR */
/*----------------------------------------------------------------------
Load/Save Functions
----------------------------------------------------------------------*/
int as_save (const ATTSET *set, FILE *file)
{ /* --- save instances and weight */
int i; /* loop variable */
ATT **p; /* to traverse attribute vector */
assert(set && file); /* check the function arguments */
p = set->atts; /* write attribute instantiations */
for (i = set->attcnt; --i >= 0; p++)
fwrite(&(*p)->inst, sizeof(INST), 1, file);
fwrite(&set->weight, sizeof(float), 1, file);
return ferror(file); /* check for write error */
} /* as_save() */
/*--------------------------------------------------------------------*/
int as_load (ATTSET *set, FILE *file)
{ /* --- load instances and weight */
int i; /* loop variable */
ATT **p; /* to traverse attribute vector */
assert(set && file); /* check the function arguments */
p = set->atts; /* read attribute instantiations */
for (i = set->attcnt; --i >= 0; p++)
fread(&(*p)->inst, sizeof(INST), 1, file);
fread(&set->weight, sizeof(float), 1, file);
if (ferror(file)) return -1; /* check for a read error */
return (feof(file)) ? 1 : 0; /* check for end of file */
} /* as_load() */
/*----------------------------------------------------------------------
Read/Write Functions
----------------------------------------------------------------------*/
#ifdef AS_RDWR
CCHAR* as_chars (ATTSET *set, CCHAR *blanks,
CCHAR *fldseps, CCHAR *recseps, CCHAR *uvchars)
{ /* --- set special characters */
assert(set); /* check the function argument */
if (blanks) /* set blank characters */
set->chars[0] = tfs_chars(set->tfscan, TFS_BLANK, blanks);
if (fldseps) /* set field separators */
set->chars[1] = tfs_chars(set->tfscan, TFS_FLDSEP, fldseps);
if (recseps) /* set record separators */
set->chars[2] = tfs_chars(set->tfscan, TFS_RECSEP, recseps);
if (uvchars) /* set unknown value characters */
set->chars[3] = tfs_chars(set->tfscan, TFS_OTHER, uvchars);
return set->chars; /* return the output characters */
} /* as_chars() */
/*--------------------------------------------------------------------*/
static int _rderr (ATTSET *set, int code, int fld, int exp, char *s)
{ /* --- set read error information */
set->err->code = code; /* note the error code and */
set->err->rec = 0; /* clear the record number */
set->err->fld = fld; /* note the current field and */
set->err->exp = exp; /* the expected number of fields */
if (s) sc_format(set->buf, s, 1);
else set->buf[0] = '\0'; /* format the string value */
return code; /* return the error code */
} /* _rderr() */
/*--------------------------------------------------------------------*/
int as_read (ATTSET *set, FILE *file, int mode, ...)
{ /* --- read attributes/instances */
va_list args; /* list of variable arguments */
int i, r; /* loop variable, buffer */
int off, cnt, end; /* range of attributes */
char *name; /* attribute name */
int attid; /* attribute identifier */
ATT *att, **p; /* to traverse the attributes */
int vsz; /* field vector size */
int *fld; /* pointer to/in field vector */
double wgt; /* instantiation weight */
char *s; /* end pointer for conversion */
int d; /* delimiter type */
INST *inst; /* dummy instance */
char buf[AS_MAXLEN+1]; /* read buffer */
char dflt[32]; /* buffer for default name */
assert(set); /* check the function argument */
if (!file) { /* if no file is given */
#if defined AS_FLDS || defined AS_RDWR
if (set->flds) { free(set->flds); set->flds = NULL; }
set->fldcnt = set->fldvsz = 0;
#endif /* delete the field map */
return set->err->code = 0; /* and abort the function */
}
inst = (mode & AS_NOXVAL) ? (void*)1 : NULL;
/* --- get range of attributes --- */
if (mode & AS_RANGE) { /* if an index range is given */
va_start(args, mode); /* start variable argument evaluation */
off = va_arg(args, int); /* get offset to first value */
cnt = va_arg(args, int); /* and number of values */
va_end(args); /* end variable argument evaluation */
i = set->attcnt -off; /* check and adapt */
if (cnt > i) cnt = i; /* number of values */
assert((off >= 0) && (cnt >= 0)); }
else { /* if no index range given */
off = 0; cnt = set->attcnt; /* get full index range */
}
end = off +cnt; /* get index of last attribute */
/* --- read attributes --- */
if (mode & (AS_ATT|AS_DFLT)){ /* if to read attributes */
for (p = set->atts +(i = set->attcnt); --i >= 0; )
(*--p)->read = 0; /* clear all read flags */
vsz = set->fldvsz; /* get size of field vector */
cnt = set->fldcnt = 0; /* initialize field counter */
do { /* traverse table fields */
d = tfs_getfld(set->tfscan, file, buf, AS_MAXLEN);
if (d < 0) return _rderr(set, E_FREAD, cnt+1, 0, NULL);
if ((buf[0] == 0) /* get next field name and */
&& !(mode & AS_DFLT)) /* check whether it is empty */
return _rderr(set, E_EMPFLD, cnt+1, 0, NULL);
if ((mode & AS_WEIGHT) && (d <= TFS_REC))
break; /* skip weight in last field */
if (cnt >= vsz) { /* if the field vector is full */
vsz += (vsz > BLKSIZE) ? (vsz >> 1) : BLKSIZE;
fld = (int*)realloc(set->flds, vsz *sizeof(int));
if (!fld) return _rderr(set, E_NOMEM, cnt+1, 0, NULL);
set->flds = fld; /* resize the field vector */
set->fldvsz = vsz; /* and set new field vector */
} /* and its size */
if (mode & AS_DFLT) /* in default mode create a name */
sprintf(name = dflt, "%d", cnt+1);
else name = buf; /* otherwise use the name read */
attid = as_attid(set, name); /* get the attribute id */
if (attid >= 0) { /* if the attribute exists, */
att = set->atts[attid]; /* get the attribute and */
if (att->read) /* check its read flag */
return _rderr(set, E_DUPFLD, cnt+1, 0, name);
if ((attid < off) /* if the attribute */
|| (attid >= end) /* is out of range */
|| ((mode & AS_MARKED) /* or if in marked mode and */
&& (att->mark < 0))) /* the attribute is not marked, */
attid = -1; /* skip this attribute, */
else { /* otherwise (if to read attribute) */
att->read = -1; /* set the read flag */
} } /* if the attribute does not exist */
else if (mode & AS_NOXATT)/* if not to extend the att. set, */
att = NULL; /* invalidate the attribute */
else { /* if to extend the att. set */
att = att_create(name, AT_SYM);
if (!att) return _rderr(set, E_NOMEM, cnt+1, 0, NULL);
if (as_attadd(set, att) != 0) { att_delete(att);
return _rderr(set, E_NOMEM, cnt+1, 0, NULL); }
attid = att->id; /* create an attribute, add it to */
att->read = -1; /* the set, get its identifier, */
} /* and set the read flag */
set->flds[cnt++] = attid; /* set field mapping */
if ((mode & AS_DFLT) /* if to use a default header, */
&& (attid >= 0)) { /* set the attribute value read */
if (_isunk(set->tfscan, buf)) {
if (mode & AS_NOUNKS) /* if the value is unknown */
return _rderr(set, E_VALUE, cnt, 0, buf);
if (att->type == AT_FLT) att->inst.f = UV_FLT;
else if (att->type == AT_INT) att->inst.i = UV_INT;
else att->inst.i = UV_SYM; }
else { /* if the value is known */
r = att_valadd(att, buf, inst);
if (r >= 0) continue; /* add the value to the attribute */
if (r >= -1) return _rderr(set, E_NOMEM, cnt, 0, NULL);
else return _rderr(set, E_VALUE, cnt, 0, buf);
} /* if the value cannot be added, */
} /* abort with an error code */
} while (d > TFS_REC); /* while not at end of record */
for (p = set->atts +(i = off); i < end; i++) {
att = *p++; /* traverse the attributes, */
if (att->read) continue; /* but skip read attributes */
if (!(mode & AS_MARKED) /* if there is an unread attribute */
|| (att->mark > 0)) /* that has to be read, abort */
return _rderr(set, E_MISFLD, cnt, 0, att->name);
if ((mode & AS_MARKED) /* if an unread attribute is marked */
&& (att->mark == 0)) /* as optional (read if present), */
att->mark = -1; /* adapt the attribute marker */
} /* to indicate that it is missing */
set->fldcnt = cnt; /* set number of fields */
return set->err->code = 0; /* and return 'ok' */
} /* (end of read attributes) */
/* --- read instances --- */
cnt = set->fldcnt; /* get number of fields and */
if (cnt > 0) fld = set->flds; /* a pointer to the field vector */
else { fld = NULL; cnt = set->attcnt; }
d = TFS_FLD; /* set default delimiter type */
for (i = 0; (i < cnt) && (d > TFS_REC); i++) {
d = tfs_getfld(set->tfscan, file, buf, AS_MAXLEN);
if (d <= TFS_EOF) { /* get next value */
if (d < 0) return _rderr(set, E_FREAD, i+1, 0, NULL);
if (i <= 0) return set->err->code = 1;
} /* if on first field, abort */
attid = (fld) ? *fld++ : i; /* get attribute identifier */
if ((attid < off) || (attid >= end))
continue; /* skip attributes out of range */
att = set->atts[attid]; /* get attribute */
if ((mode & AS_MARKED) /* if in marked mode and */
&& (att->mark < 0)) /* attribute is not marked, */
continue; /* skip this field */
if (_isunk(set->tfscan, buf)) {
if (att->type == AT_SYM){ /* if the attribute is symbolic, */
r = att_valid(att,buf); /* check for a known value */
if (r >= 0) { att->inst.i = r; continue; }
} /* if it is, simply set the value */
if (mode & AS_NOUNKS) /* if the value is unknown */
return _rderr(set, E_VALUE, i+1, 0, buf);
if (att->type == AT_FLT) att->inst.f = UV_FLT;
else if (att->type == AT_INT) att->inst.i = UV_INT;
else att->inst.i = UV_SYM; }
else { /* if the value is known */
r = att_valadd(att, buf, inst);
if (r >= 0) continue; /* add the value to the attribute */
if (r >= -1) return _rderr(set, E_NOMEM, i+1, 0, NULL);
else return _rderr(set, E_VALUE, i+1, 0, buf);
} /* if the value cannot be added, */
} /* for (i = 0; .. */ /* abort with an error code */
if (!(mode & AS_WEIGHT)) /* if there is no weight field, */
set->weight = 1.0F; /* set instantiation weight to 1 */
else if (d <= TFS_REC) /* if no weight field is available */
return _rderr(set, E_FLDCNT, cnt, cnt+1, NULL);
else { /* if weight field is available */
d = tfs_getfld(set->tfscan, file, buf, AS_MAXLEN);
if (d < 0) return _rderr(set, E_FREAD, i+1, 0, NULL);
wgt = strtod(buf, &s); /* read weight field and convert it */
if ((s == buf) || (*s != '\0')
|| (wgt < 0) || (wgt > FLT_MAX))
return _rderr(set, E_VALUE, i+1, 0, buf);
set->weight = (float)wgt; /* set the instantiation weight */
}
if ((i < cnt) /* if too few */
|| (d > TFS_REC)) { /* or too many fields found */
cnt += ((mode & AS_WEIGHT) ? 1 : 0);
return _rderr(set, E_FLDCNT, (i < cnt) ? i : cnt+1, cnt, NULL);
} /* abort with an error code */
return set->err->code = 0; /* return 'ok' */
} /* as_read() */
/*--------------------------------------------------------------------*/
int as_write (ATTSET *set, FILE *file, int mode, ...)
{ /* --- write attributes/instances */
va_list args; /* list of variable arguments */
int i, k; /* loop variables, buffers */
int off, cnt, end; /* range of attributes */
ATT *att; /* to traverse attributes */
int attid; /* attribute identifier */
int *fld; /* pointer to/in field vector */
CCHAR *name; /* name of attribute/value */
INFOUTFN *infout = 0; /* add. info. output function */
char buf[64]; /* buffer for attribute values */
assert(set && file); /* check the function arguments */
/* --- get range of attributes --- */
va_start(args, mode); /* start variable argument evaluation */
if (mode & AS_RANGE) { /* if an index range is given */
off = va_arg(args, int); /* get offset to first value */
cnt = va_arg(args, int); /* and number of values */
i = set->attcnt -off; /* check and adapt */
if (cnt > i) cnt = i; /* number of values */
assert((off >= 0) && (cnt >= 0)); }
else { /* if no index range given */
off = 0; cnt = set->attcnt; /* get full index range */
}
end = off +cnt; /* get end of range of attributes */
if (mode & (AS_INFO1|AS_INFO2)) /* get additional information */
infout = va_arg(args, INFOUTFN*); /* output function */
va_end(args); /* get add. info. output function and */
/* --- write attributes/values --- */
if (!(mode & AS_RDORD) /* if not to write in read order */
|| (set->fldcnt <= 0)) /* or no read order is available, */
fld = NULL; /* clear field pointer */
else { /* if to write in read order, */
fld = set->flds; /* get field pointer */
cnt = set->fldcnt; /* and number of fields */
}
name = NULL; /* clear name as a first field flag */
for (i = 0; i < cnt; i++) { /* traverse fields/attributes */
attid = (fld) ? *fld++ : (i +off); /* get next index */
if ((attid < off) || (attid >= end))
continue; /* skip attributes out of range */
att = set->atts[attid]; /* get attribute */
if ((mode & AS_MARKED) /* if in marked mode and */
&& (att->mark < 0)) /* attribute is not marked, */
continue; /* skip this attribute */
if (name) /* if not the first field, */
putc(set->chars[1], file);/* print field separator */
if (mode & AS_ATT) /* if to write attributes, */
name = att->name; /* get attribute name */
else if (att->type == AT_INT) { /* if integer value */
if (att->inst.i <= UV_INT)/* if value is unknown, */
name = set->chars +3; /* set unknown value character */
else { /* uf value is known */
sprintf(buf, "%d", att->inst.i); name = buf;
} } /* format value */
else if (att->type == AT_FLT) { /* if real/float value */
if (att->inst.f <= UV_FLT)/* if value is unknown, */
name = set->chars +3; /* set unknown value character */
else { /* if value is known */
sprintf(buf, "%g", att->inst.f); name = buf;
} } /* format value */
else { /* otherwise (symbolic value) */
k = att->inst.i; /* get value identifier */
name = (k < 0) ? set->chars +3 : att->vals[k]->name;
} /* get name of attribute value */
fputs(name, file); /* write attribute/value name */
if ((mode & (AS_ALIGN|AS_ALNHDR)) /* if to align fields and */
&& ((i < cnt -1) /* not on the last field to write */
|| (mode & (AS_INFO1|AS_WEIGHT|AS_INFO2)))) {
k = att->valwd[0]; /* get width of widest value */
if ((mode & AS_ALNHDR) && (att->attwd[0] > k))
k = att->attwd[0]; /* adapt with width of att. name and */
k -= (int)strlen(name); /* subtract width of current value */
while (--k >= 0) putc(set->chars[0], file);
} /* pad the field with blanks */
} /* (write normal fields) */
/* --- write weight and additional information --- */
if (mode & AS_INFO1) { /* if to write additional information */
putc(set->chars[1], file); /* write field separator */
infout(set, file, mode, set->chars);
} /* call function to write add. info. */
if (mode & AS_WEIGHT) { /* if weight output requested */
putc(set->chars[1], file); /* write field separator */
if (mode & AS_ATT) putc('#', file);
else fprintf(file, "%g", set->weight);
} /* write counter field */
if (mode & AS_INFO2) { /* if to write additional information */
putc(set->chars[1], file); /* write field separator */
infout(set, file, mode, set->chars);
} /* call function to write add. info. */
putc(set->chars[2], file); /* terminate the record written */
return ferror(file); /* check for a write error */
} /* as_write() */
#endif /* #ifdef AS_RDWR */
/*----------------------------------------------------------------------
Description Function
----------------------------------------------------------------------*/
int as_desc (ATTSET *set, FILE *file, int mode, int maxlen, ...)
{ /* --- describe an attribute set */
va_list args; /* list of variable arguments */
int i, k; /* loop variables, buffers */
int off, cnt; /* range of attributes */
ATT *att; /* to traverse attributes */
int len; /* length of value name */
int pos; /* position in output line */
char buf[4*AS_MAXLEN+4]; /* output buffer */
assert(set && file); /* check the function arguments */
/* --- get range of attributes --- */
if (mode & AS_RANGE) { /* if an index range is given */
va_start(args, maxlen); /* start variable argument evaluation */
off = va_arg(args, int); /* get offset to first value */
cnt = va_arg(args, int); /* and number of values */
va_end(args); /* end variable argument evaluation */
i = set->attcnt -off; /* check and adapt */
if (cnt > i) cnt = i; /* number of values */
assert((off >= 0) && (cnt >= 0)); }
else { /* if no index range given, */
off = 0; cnt = set->attcnt; /* get full index range */
}
/* --- print header (as a comment) --- */
if (mode & AS_TITLE) { /* if title flag is set */
i = k = (maxlen > 0) ? maxlen -2 : 70;
fputs("/*", file); while (--i >= 0) putc('-', file);
fprintf(file, "\n %s\n", set->name);
while (--k >= 0) putc('-', file); fputs("*/\n", file);
} /* print a title header */
if (maxlen <= 0) maxlen = INT_MAX;
/* --- print attribute domains --- */
for (i = 0; i < cnt; i++) { /* traverse the attributes */
att = set->atts[off +i]; /* get the next attribute */
if ((mode & AS_MARKED) /* if in marked mode and */
&& (att->mark < 0)) /* attribute is not marked, */
continue; /* skip this attribute */
sc_format(buf, att->name, 0); /* print the name */
fprintf(file, "dom(%s) = ", buf);
if (att->type == AT_INT) { /* if attribute is integer valued */
fputs("ZZ", file); /* print integer numbers sign */
if ((mode & AS_IVALS) /* if intervals requested */
&& (att->min.i <= att->max.i))
fprintf(file, " [%d, %d]", att->min.i, att->max.i);
fputs(";\n", file); /* print range of values */
continue; /* terminate line and */
} /* continue with next attribute */
if (att->type == AT_FLT) { /* if attribute is real valued */
fputs("IR", file); /* print real numbers sign */
if ((mode & AS_IVALS) /* if intervals requested */
&& (att->min.f <= att->max.f))
fprintf(file, " [%g, %g]", att->min.f, att->max.f);
fputs(";\n", file); /* print range of values */
continue; /* terminate line and */
} /* continue with next attribute */
fputs("{", file); /* if attribute is symbolic */
pos = att->attwd[1] +10; /* initialize position */
for (k = 0; k < att->valcnt; k++) {
if (k > 0) { /* if this is not the first value, */
putc(',', file); pos++; } /* print a separator */
len = sc_format(buf, att->vals[k]->name, 0);
if ((pos +len > maxlen-4) /* if line would get too long, */
&& (pos > 2)) { /* start a new line */
fputs("\n ", file); pos = 1; }
putc(' ', file); /* print a separating blank and */
fputs(buf, file); /* the (scanable) value name, */
pos += len +1; /* then calculate new position */
}
fputs(" };\n", file); /* terminate domain and line */
}
/* --- print attribute weights --- */
if (mode & AS_WEIGHT) { /* if to write weights, */
putc('\n', file); /* leave one line empty */
for (i = 0; i < cnt; i++) { /* traverse attributes again */
att = set->atts[off +i]; /* get the next attribute */
if ((mode & AS_MARKED) /* if in marked mode and */
&& (att->mark < 0)) /* attribute is not marked, */
continue; /* skip this attribute */
sc_format(buf, att->name, 0);
fprintf(file, "wgt(%s) = %g;\n", buf, att->weight);
} /* print attribute weights */
}
return ferror(file); /* check for a write error */
} /* as_desc() */
/*----------------------------------------------------------------------
Additional Functions
----------------------------------------------------------------------*/
#ifndef NDEBUG
void as_stats (const ATTSET *set)
{ /* --- compute and print statistics */
const ATT *att = NULL; /* to traverse attributes */
const VAL *val; /* to traverse values */
int i, k; /* loop variables */
int cnt; /* number of attributes/values */
int size; /* size of hash table */
int used; /* number of used hash buckets */
int len; /* length of current bucket list */
int min, max; /* min. and max. bucket list length */
int lcs[10]; /* counter for bucket list lengths */
assert(set); /* check for a valid attribute set */
if (set->attvsz <= 0) return; /* check hash table size */
for (i = -1; i < set->attcnt; i++) {
min = INT_MAX; max = used = 0; /* initialize variables */
for (k = 10; --k >= 0; ) lcs[k] = 0;
if (i < 0) { /* statistics for attribute set */
printf("attribute set \"%s\"\n", set->name);
size = set->attvsz; /* get hash table size */
cnt = set->attcnt; } /* and number of attributes */
else { /* statistics for an attribute */
att = set->atts[i]; /* get attribute and check it */
if ((att->type != AT_SYM) || (att->valvsz <= 0)) continue;
printf("\nattribute \"%s\"\n", att->name);
size = att->valvsz; /* print attribute name */
cnt = att->valcnt; /* and get hash table size */
} /* and number of values */
for (k = size; --k >= 0; ){ /* traverse bucket vector */
len = 0; /* determine bucket list length */
if (i < 0) for (att = set->htab[k]; att; att = att->succ) len++;
else for (val = att->htab[k]; val; val = val->succ) len++;
if (len > 0) used++; /* count used hash buckets */
if (len < min) min = len; /* determine minimal and */
if (len > max) max = len; /* maximal list length */
lcs[(len >= 9) ? 9 : len]++;
} /* count list length */
printf("number of objects : %d\n", cnt);
printf("number of hash buckets: %d\n", size);
printf("used hash buckets : %d\n", used);
printf("minimal list length : %d\n", min);
printf("maximal list length : %d\n", max);
printf("average list length : %g\n", (double)cnt/size);
printf("ditto, of used buckets: %g\n", (double)cnt/used);
printf("length distribution :\n");
for (k = 0; k < 9; k++) printf("%3d ", k);
printf(" >8\n");
for (k = 0; k < 9; k++) printf("%3d ", lcs[k]);
printf("%3d\n", lcs[9]);
}
} /* as_stats() */
#endif