📄 assocfunc.c
字号:
/* * Copyright (c) 1994 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Association table routines. * An association table is a type of value which can be "indexed" by * one or more arbitrary values. Each element in the table is thus an * association between a particular set of index values and a result value. * The elements in an association table are stored in a hash table for * quick access. */#include "value.h"#define MINHASHSIZE 31 /* minimum size of hash tables */#define GROWHASHSIZE 50 /* approximate growth for hash tables */#define CHAINLENGTH 10 /* desired number of elements on a hash chain */#define ELEMSIZE(n) (sizeof(ASSOCELEM) + (sizeof(VALUE) * ((n) - 1)))static ASSOCELEM *elemindex MATH_PROTO((ASSOC *ap, long index));static BOOL compareindices MATH_PROTO((VALUE *v1, VALUE *v2, long dim));static void resize MATH_PROTO((ASSOC *ap, long newsize));static void assoc_elemfree MATH_PROTO((ASSOCELEM *ep));static long nextprime MATH_PROTO((long n));/* * Return the address of the value specified by normal indexing of * an association. The create flag is TRUE if a value is going to be * assigned into the specified indexing location. If create is FALSE and * the index value doesn't exist, a pointer to a NULL value is returned. */VALUE *associndex(ap, create, dim, indices) ASSOC *ap; /* association to index into */ BOOL create; /* whether to create the index value */ long dim; /* dimension of the indexing */ VALUE *indices; /* table of values being indexed by */{ ASSOCELEM **listhead; ASSOCELEM *ep; static VALUE val; HASH hash; int i; if (dim <= 0) math_error("No dimensions for indexing association"); /* * Calculate the hash value to use for this set of indices * so that we can first select the correct hash chain, and * also so we can quickly compare each element for a match. */ hash = 0; for (i = 0; i < dim; i++) /* ignore Saber-C warning about Over/underflow */ hash = hash * 67319821 + hashvalue(&indices[i]); /* * Search the correct hash chain for the specified set of indices. * If found, return the address of the found element's value. */ listhead = &ap->a_table[hash % ap->a_size]; for (ep = *listhead; ep; ep = ep->e_next) { if ((ep->e_hash != hash) || (ep->e_dim != dim)) continue; if (compareindices(ep->e_indices, indices, dim)) return &ep->e_value; } /* * The set of indices was not found. * Either return a pointer to a NULL value for a read reference, * or allocate a new element in the list for a write reference. */ if (!create) { val.v_type = V_NULL; return &val; } ep = (ASSOCELEM *) malloc(ELEMSIZE(dim)); if (ep == NULL) math_error("Cannot allocate association element"); ep->e_dim = dim; ep->e_hash = hash; ep->e_value.v_type = V_NULL; for (i = 0; i < dim; i++) copyvalue(&indices[i], &ep->e_indices[i]); ep->e_next = *listhead; *listhead = ep; ap->a_count++; resize(ap, ap->a_count / CHAINLENGTH); return &ep->e_value;}/* * Search an association for the specified value starting at the * specified index. Returns the element number (zero based) of the * found value, or -1 if the value was not found. */longassocsearch(ap, vp, index) ASSOC *ap; VALUE *vp; long index;{ ASSOCELEM *ep; if (index < 0) index = 0; while (TRUE) { ep = elemindex(ap, index); if (ep == NULL) return -1; if (!comparevalue(&ep->e_value, vp)) return index; index++; }}/* * Search an association backwards for the specified value starting at the * specified index. Returns the element number (zero based) of the * found value, or -1 if the value was not found. */longassocrsearch(ap, vp, index) ASSOC *ap; VALUE *vp; long index;{ ASSOCELEM *ep; if (index >= ap->a_count) index = ap->a_count - 1; while (TRUE) { ep = elemindex(ap, index); if (ep == NULL) return -1; if (!comparevalue(&ep->e_value, vp)) return index; index--; }}/* * Return the address of an element of an association indexed by the * double-bracket operation. */static ASSOCELEM *elemindex(ap, index) ASSOC *ap; /* association to index into */ long index; /* index of desired element */{ ASSOCELEM *ep; int i; if ((index < 0) || (index > ap->a_count)) return NULL; /* * This loop should be made more efficient by remembering * previously requested locations within the association. */ for (i = 0; i < ap->a_size; i++) { for (ep = ap->a_table[i]; ep; ep = ep->e_next) { if (index-- == 0) return ep; } } return NULL;}/* * Return the address of the value specified by double-bracket indexing * of an association. Returns NULL if there is no such element. */VALUE *assocfindex(ap, index) ASSOC *ap; /* association to index into */ long index; /* index of desired element */{ ASSOCELEM *ep; ep = elemindex(ap, index); if (ep == NULL) return NULL; return &ep->e_value;}/* * Compare two associations to see if they are identical. * Returns TRUE if they are different. */BOOLassoccmp(ap1, ap2) ASSOC *ap1, *ap2;{ ASSOCELEM **table1; ASSOCELEM *ep1; ASSOCELEM *ep2; long size1; long size2; HASH hash; long dim; if (ap1 == ap2) return FALSE; if (ap1->a_count != ap2->a_count) return TRUE; table1 = ap1->a_table; size1 = ap1->a_size; size2 = ap2->a_size; while (size1-- > 0) { for (ep1 = *table1++; ep1; ep1 = ep1->e_next) { hash = ep1->e_hash; dim = ep1->e_dim; for (ep2 = ap2->a_table[hash % size2]; ; ep2 = ep2->e_next) { if (ep2 == NULL) return TRUE; if (ep2->e_hash != hash) continue; if (ep2->e_dim != dim) continue; if (compareindices(ep1->e_indices, ep2->e_indices, dim)) break; } if (comparevalue(&ep1->e_value, &ep2->e_value)) return TRUE; } } return FALSE;}/* * Copy an association value. */ASSOC *assoccopy(oldap) ASSOC *oldap;{ ASSOC *ap; ASSOCELEM *oldep; ASSOCELEM *ep; ASSOCELEM **listhead; int oldhi; int i; ap = assocalloc(oldap->a_count / CHAINLENGTH); ap->a_count = oldap->a_count; for (oldhi = 0; oldhi < oldap->a_size; oldhi++) { for (oldep = oldap->a_table[oldhi]; oldep; oldep = oldep->e_next) { ep = (ASSOCELEM *) malloc(ELEMSIZE(oldep->e_dim)); if (ep == NULL) math_error("Cannot allocate association element"); ep->e_dim = oldep->e_dim; ep->e_hash = oldep->e_hash; ep->e_value.v_type = V_NULL; for (i = 0; i < ep->e_dim; i++) copyvalue(&oldep->e_indices[i], &ep->e_indices[i]); copyvalue(&oldep->e_value, &ep->e_value); listhead = &ap->a_table[ep->e_hash % ap->a_size]; ep->e_next = *listhead; *listhead = ep; } } return ap;}/* * Resize the hash table for an association to be the specified size. * This is only actually done if the growth from the previous size is * enough to make this worthwhile. */static voidresize(ap, newsize) ASSOC *ap; long newsize;{ ASSOCELEM **oldtable; ASSOCELEM **newtable; ASSOCELEM **oldlist; ASSOCELEM **newlist; ASSOCELEM *ep; int i; if (newsize < ap->a_size + GROWHASHSIZE) return; newsize = nextprime(newsize); newtable = (ASSOCELEM **) malloc(sizeof(ASSOCELEM *) * newsize); if (newtable == NULL) math_error("No memory to grow association"); for (i = 0; i < newsize; i++) newtable[i] = NULL; oldtable = ap->a_table; oldlist = oldtable; for (i = 0; i < ap->a_size; i++) { while (*oldlist) { ep = *oldlist; *oldlist = ep->e_next; newlist = &newtable[ep->e_hash % newsize]; ep->e_next = *newlist; *newlist = ep; } oldlist++; } ap->a_table = newtable; ap->a_size = newsize; free((char *) oldtable);}/* * Free an association element, along with any contained values. */static voidassoc_elemfree(ep) ASSOCELEM *ep;{ int i; for (i = 0; i < ep->e_dim; i++) freevalue(&ep->e_indices[i]); freevalue(&ep->e_value); ep->e_dim = 0; ep->e_next = NULL; free((char *) ep);}/* * Allocate a new association value with an initial hash table. * The hash table size is set at specified (but at least a minimum size). */ASSOC *assocalloc(initsize) long initsize;{ register ASSOC *ap; int i; if (initsize < MINHASHSIZE) initsize = MINHASHSIZE; ap = (ASSOC *) malloc(sizeof(ASSOC)); if (ap == NULL) math_error("No memory for association"); ap->a_count = 0; ap->a_size = initsize; ap->a_table = (ASSOCELEM **) malloc(sizeof(ASSOCELEM *) * initsize); if (ap->a_table == NULL) { free((char *) ap); math_error("No memory for association"); } for (i = 0; i < initsize; i++) ap->a_table[i] = NULL; return ap;}/* * Free an association value, along with all of its elements. */voidassocfree(ap) register ASSOC *ap;{ ASSOCELEM **listhead; ASSOCELEM *ep; ASSOCELEM *nextep; int i; listhead = ap->a_table; for (i = 0; i < ap->a_size; i++) { nextep = *listhead; *listhead = NULL; while (nextep) { ep = nextep; nextep = ep->e_next; assoc_elemfree(ep); } listhead++; } free((char *) ap->a_table); ap->a_table = NULL; free((char *) ap);}/* * Print out an association along with the specified number of * its elements. The elements are printed out in shortened form. */voidassocprint(ap, max_print) ASSOC *ap; long max_print;{ ASSOCELEM *ep; long index; long i; int savemode; if (max_print <= 0) { math_fmt("assoc (%ld element%s)", ap->a_count, ((ap->a_count == 1) ? "" : "s")); return; } math_fmt("\n assoc (%ld element%s):\n", ap->a_count, ((ap->a_count == 1) ? "" : "s")); for (index = 0; ((index < max_print) && (index < ap->a_count)); index++) { ep = elemindex(ap, index); if (ep == NULL) continue; math_str(" ["); for (i = 0; i < ep->e_dim; i++) { if (i) math_chr(','); savemode = math_setmode(MODE_FRAC); printvalue(&ep->e_indices[i], (PRINT_SHORT | PRINT_UNAMBIG)); math_setmode(savemode); } math_str("] = "); printvalue(&ep->e_value, PRINT_SHORT | PRINT_UNAMBIG); math_chr('\n'); } if (max_print < ap->a_count) math_str(" ...\n");}/* * Return a trivial hash value for an association. */HASHassochash(ap) ASSOC *ap;{ return ap->a_count * 700001;}/* * Compare two lists of index values to see if they are identical. * Returns TRUE if they are the same. */static BOOLcompareindices(v1, v2, dim) VALUE *v1; VALUE *v2; long dim;{ int i; for (i = 0; i < dim; i++) if (v1[i].v_type != v2[i].v_type) return FALSE; while (dim-- > 0) if (comparevalue(v1++, v2++)) return FALSE; return TRUE;}/* * Return the next prime number up from the specified value. * This is used to pick a good hash table size. */static longnextprime(n) long n;{ long i; if ((n & 0x01) == 0) n++; while (TRUE) { for (i = 3; n % i; i += 2) { if (i * i > n) return n; } n += 2; }}/* END CODE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -