diff options
Diffstat (limited to 'blt/src/bltVecCmd.c')
-rw-r--r-- | blt/src/bltVecCmd.c | 1978 |
1 files changed, 1978 insertions, 0 deletions
diff --git a/blt/src/bltVecCmd.c b/blt/src/bltVecCmd.c new file mode 100644 index 00000000000..df1c1297f5d --- /dev/null +++ b/blt/src/bltVecCmd.c @@ -0,0 +1,1978 @@ +/* + * bltVecCmd.c -- + * + * This module implements vector data objects. + * + * Copyright 1995-1998 Lucent Technologies, Inc. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and warranty + * disclaimer appear in supporting documentation, and that the names + * of Lucent Technologies any of their entities not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. + * + * Lucent Technologies disclaims all warranties with regard to this + * software, including all implied warranties of merchantability and + * fitness. In no event shall Lucent Technologies be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether in + * an action of contract, negligence or other tortuous action, arising + * out of or in connection with the use or performance of this + * software. + */ + +/* + * TODO: + * o Add H. Kirsch's vector binary read operation + * x binread file0 + * x binread -file file0 + * + * o Add ASCII/binary file reader + * x read fileName + * + * o Allow Tcl-based client notifications. + * vector x + * x notify call Display + * x notify delete Display + * x notify reorder #1 #2 + */ + +#include "bltVecInt.h" + +#if (TCL_MAJOR_VERSION == 7) + +static void +GetValues(vPtr, first, last, resultPtr) + VectorObject *vPtr; + int first, last; + Tcl_DString *resultPtr; +{ + register int i; + char valueString[TCL_DOUBLE_SPACE + 1]; + + for (i = first; i <= last; i++) { + Tcl_PrintDouble(vPtr->interp, vPtr->valueArr[i], valueString); + Tcl_DStringAppendElement(resultPtr, valueString); + } +} + +static void +ReplicateValue(vPtr, first, last, value) + VectorObject *vPtr; + int first, last; + double value; +{ + register int i; + for (i = first; i <= last; i++) { + vPtr->valueArr[i] = value; + } + vPtr->notifyFlags |= UPDATE_RANGE; +} + +static int +CopyList(vPtr, nElem, elemArr) + VectorObject *vPtr; + int nElem; + char **elemArr; +{ + register int i; + double value; + + if (Blt_VectorChangeLength(vPtr, nElem) != TCL_OK) { + return TCL_ERROR; + } + for (i = 0; i < nElem; i++) { + if (Tcl_GetDouble(vPtr->interp, elemArr[i], &value)!= TCL_OK) { + vPtr->length = i; + return TCL_ERROR; + } + vPtr->valueArr[i] = value; + } + return TCL_OK; +} + +static int +AppendVector(destPtr, srcPtr) + VectorObject *destPtr, *srcPtr; +{ + int nBytes; + int oldSize, newSize; + + oldSize = destPtr->length; + newSize = oldSize + srcPtr->last - srcPtr->first + 1; + if (Blt_VectorChangeLength(destPtr, newSize) != TCL_OK) { + return TCL_ERROR; + } + nBytes = (newSize - oldSize) * sizeof(double); + memcpy((char *)(destPtr->valueArr + oldSize), + (srcPtr->valueArr + srcPtr->first), nBytes); + destPtr->notifyFlags |= UPDATE_RANGE; + return TCL_OK; +} + +static int +AppendList(vPtr, nElem, elemArr) + VectorObject *vPtr; + int nElem; + char **elemArr; +{ + int count; + register int i; + double value; + int oldSize; + + oldSize = vPtr->length; + if (Blt_VectorChangeLength(vPtr, vPtr->length + nElem) != TCL_OK) { + return TCL_ERROR; + } + count = oldSize; + for (i = 0; i < nElem; i++) { + if (Tcl_ExprDouble(vPtr->interp, elemArr[i], &value) + != TCL_OK) { + vPtr->length = count; + return TCL_ERROR; + } + vPtr->valueArr[count++] = value; + } + vPtr->notifyFlags |= UPDATE_RANGE; + return TCL_OK; +} + +/* Vector instance option commands */ + +/* + * ----------------------------------------------------------------------- + * + * AppendOp -- + * + * Appends one of more Tcl lists of values, or vector objects + * onto the end of the current vector object. + * + * Results: + * A standard Tcl result. If a current vector can't be created, + * resized, any of the named vectors can't be found, or one of + * lists of values is invalid, TCL_ERROR is returned. + * + * Side Effects: + * Clients of current vector will be notified of the change. + * + * ----------------------------------------------------------------------- + */ +static int +AppendOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + register int i; + int result; + VectorObject *v2Ptr; + + for (i = 2; i < argc; i++) { + v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, + argv[i], (char **)NULL, NS_SEARCH_BOTH); + if (v2Ptr != NULL) { + result = AppendVector(vPtr, v2Ptr); + } else { + int nElem; + char **elemArr; + + if (Tcl_SplitList(interp, argv[i], &nElem, &elemArr) != TCL_OK) { + return TCL_ERROR; + } + result = AppendList(vPtr, nElem, elemArr); + Blt_Free(elemArr); + } + if (result != TCL_OK) { + return TCL_ERROR; + } + } + if (argc > 2) { + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + Blt_VectorUpdateClients(vPtr); + } + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * ClearOp -- + * + * Deletes all the accumulated array indices for the Tcl array + * associated will the vector. This routine can be used to + * free excess memory from a large vector. + * + * Results: + * Always returns TCL_OK. + * + * Side Effects: + * Memory used for the entries of the Tcl array variable is freed. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +ClearOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + Blt_VectorFlushCache(vPtr); + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * DeleteOp -- + * + * Deletes the given indices from the vector. If no indices are + * provided the entire vector is deleted. + * + * Results: + * A standard Tcl result. If any of the given indices is invalid, + * interp->result will an error message and TCL_ERROR is returned. + * + * Side Effects: + * The clients of the vector will be notified of the vector + * deletions. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +DeleteOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + unsigned char *unsetArr; + register int i, j; + register int count; + + if (argc == 2) { + Blt_VectorFree(vPtr); + return TCL_OK; + } + /* + * Allocate an "unset" bitmap the size of the vector. We should + * try to use bit fields instead of a character array, since + * memory may be an issue if the vector is large. + */ + unsetArr = Blt_Calloc(sizeof(unsigned char), vPtr->length); + assert(unsetArr); + for (i = 2; i < argc; i++) { + if (Blt_VectorGetIndexRange(interp, vPtr, argv[i], + (INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL) + != TCL_OK) { + Blt_Free(unsetArr); + return TCL_ERROR; + } + for (j = vPtr->first; j <= vPtr->last; j++) { + unsetArr[j] = TRUE; + } + } + count = 0; + for (i = 0; i < vPtr->length; i++) { + if (unsetArr[i]) { + continue; + } + if (count < i) { + vPtr->valueArr[count] = vPtr->valueArr[i]; + } + count++; + } + Blt_Free(unsetArr); + vPtr->length = count; + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + Blt_VectorUpdateClients(vPtr); + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * DupOp -- + * + * Creates one or more duplicates of the vector object. + * + * Results: + * A standard Tcl result. If a new vector can't be created, + * or and existing vector resized, TCL_ERROR is returned. + * + * Side Effects: + * Clients of existing vectors will be notified of the change. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +DupOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; /* Not used. */ + int argc; + char **argv; +{ + VectorObject *v2Ptr; + int isNew; + register int i; + + for (i = 2; i < argc; i++) { + v2Ptr = Blt_VectorCreate(vPtr->dataPtr, argv[i], argv[i], argv[i], + &isNew); + if (v2Ptr == NULL) { + return TCL_ERROR; + } + if (v2Ptr == vPtr) { + continue; + } + if (Blt_VectorDuplicate(v2Ptr, vPtr) != TCL_OK) { + return TCL_ERROR; + } + if (!isNew) { + if (v2Ptr->flush) { + Blt_VectorFlushCache(v2Ptr); + } + Blt_VectorUpdateClients(v2Ptr); + } + } + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * IndexOp -- + * + * Sets or reads the value of the index. This simulates what the + * vector's variable does. + * + * Results: + * A standard Tcl result. If the index is invalid, + * interp->result will an error message and TCL_ERROR is returned. + * Otherwise interp->result will contain the values. + * + * ----------------------------------------------------------------------- + */ +static int +IndexOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + int first, last; + + if (Blt_VectorGetIndexRange(interp, vPtr, argv[2], INDEX_ALL_FLAGS, + (Blt_VectorIndexProc **) NULL) != TCL_OK) { + return TCL_ERROR; + } + first = vPtr->first, last = vPtr->last; + if (argc == 3) { + Tcl_DString dString; + + if (first == vPtr->length) { + Tcl_AppendResult(interp, "can't get index \"", argv[2], "\"", + (char *)NULL); + return TCL_ERROR; /* Can't read from index "++end" */ + } + Tcl_DStringInit(&dString); + GetValues(vPtr, first, last, &dString); + Tcl_DStringResult(interp, &dString); + Tcl_DStringFree(&dString); + } else { + char string[TCL_DOUBLE_SPACE + 1]; + double value; + + if (first == SPECIAL_INDEX) { + Tcl_AppendResult(interp, "can't set index \"", argv[2], "\"", + (char *)NULL); + return TCL_ERROR; /* Tried to set "min" or "max" */ + } + if (Tcl_ExprDouble(interp, argv[3], &value) != TCL_OK) { + return TCL_ERROR; + } + if (first == vPtr->length) { + if (Blt_VectorChangeLength(vPtr, vPtr->length + 1) != TCL_OK) { + return TCL_ERROR; + } + } + ReplicateValue(vPtr, first, last, value); + + Tcl_PrintDouble(interp, value, string); + Tcl_SetResult(interp, string, TCL_VOLATILE); + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + Blt_VectorUpdateClients(vPtr); + } + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * LengthOp -- + * + * Returns the length of the vector. If a new size is given, the + * vector is resized to the new vector. + * + * Results: + * A standard Tcl result. If the new length is invalid, + * interp->result will an error message and TCL_ERROR is returned. + * Otherwise interp->result will contain the length of the vector. + * + * ----------------------------------------------------------------------- + */ +static int +LengthOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + if (argc == 3) { + int size; + + if (Tcl_GetInt(interp, argv[2], &size) != TCL_OK) { + return TCL_ERROR; + } + if (size < 0) { + Tcl_AppendResult(interp, "bad vector size \"", argv[3], "\"", + (char *)NULL); + return TCL_ERROR; + } + if (Blt_VectorChangeLength(vPtr, size) != TCL_OK) { + return TCL_ERROR; + } + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + Blt_VectorUpdateClients(vPtr); + } + Tcl_SetResult(interp, Blt_Itoa(vPtr->length), TCL_VOLATILE); + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * MapOp -- + * + * Queries or sets the offset of the array index from the base + * address of the data array of values. + * + * Results: + * A standard Tcl result. If the source vector doesn't exist + * or the source list is not a valid list of numbers, TCL_ERROR + * returned. Otherwise TCL_OK is returned. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +MapOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; /* Not used. */ + char **argv; +{ + if (argc > 2) { + if (Blt_VectorMapVariable(interp, vPtr, argv[2]) != TCL_OK) { + return TCL_ERROR; + } + } + if (vPtr->arrayName != NULL) { + Tcl_SetResult(interp, vPtr->arrayName, TCL_VOLATILE); + } + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * MergeOp -- + * + * Merges the values from the given vectors to the current vector. + * + * Results: + * A standard Tcl result. If any of the given vectors differ in size, + * TCL_ERROR is returned. Otherwise TCL_OK is returned and the + * vector data will contain merged values of the given vectors. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +MergeOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + VectorObject *v2Ptr; + VectorObject **vecArr; + register VectorObject **vPtrPtr; + int refSize, length, nElem; + register int i; + double *valuePtr, *valueArr; + + /* Allocate an array of vector pointers of each vector to be + * merged in the current vector. */ + vecArr = Blt_Malloc(sizeof(VectorObject *) * argc); + assert(vecArr); + vPtrPtr = vecArr; + + refSize = -1; + nElem = 0; + for (i = 2; i < argc; i++) { + if (Blt_VectorLookupName(vPtr->dataPtr, argv[i], &v2Ptr) != TCL_OK) { + Blt_Free(vecArr); + return TCL_ERROR; + } + /* Check that all the vectors are the same length */ + length = v2Ptr->last - v2Ptr->first + 1; + if (refSize < 0) { + refSize = length; + } else if (length != refSize) { + Tcl_AppendResult(vPtr->interp, "vector \"", v2Ptr->name, + "\" has inconsistent length", (char *)NULL); + Blt_Free(vecArr); + return TCL_ERROR; + } + *vPtrPtr++ = v2Ptr; + nElem += refSize; + } + *vPtrPtr = NULL; + valueArr = Blt_Malloc(sizeof(double) * nElem); + if (valueArr == NULL) { + Tcl_AppendResult(vPtr->interp, "not enough memory to allocate ", + Blt_Itoa(nElem), " vector elements", (char *)NULL); + Blt_Free(vecArr); + return TCL_ERROR; + } + /* Merge the values from each of the vectors into the current vector */ + valuePtr = valueArr; + for (i = 0; i < refSize; i++) { + for (vPtrPtr = vecArr; *vPtrPtr != NULL; vPtrPtr++) { + *valuePtr++ = (*vPtrPtr)->valueArr[i + (*vPtrPtr)->first]; + } + } + Blt_Free(vecArr); + Blt_VectorReset(vPtr, valueArr, nElem, nElem, TCL_DYNAMIC); + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * NormalizeOp -- + * + * Normalizes the vector. + * + * Results: + * A standard Tcl result. If the density is invalid, TCL_ERROR + * is returned. Otherwise TCL_OK is returned. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +NormalizeOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + register int i; + double range; + + Blt_VectorUpdateRange(vPtr); + range = vPtr->max - vPtr->min; + if (argc > 2) { + VectorObject *v2Ptr; + int isNew; + + v2Ptr = Blt_VectorCreate(vPtr->dataPtr, argv[2], argv[2], argv[2], + &isNew); + if (v2Ptr == NULL) { + return TCL_ERROR; + } + if (Blt_VectorChangeLength(v2Ptr, vPtr->length) != TCL_OK) { + return TCL_ERROR; + } + for (i = 0; i < vPtr->length; i++) { + v2Ptr->valueArr[i] = (vPtr->valueArr[i] - vPtr->min) / range; + } + Blt_VectorUpdateRange(v2Ptr); + if (!isNew) { + if (v2Ptr->flush) { + Blt_VectorFlushCache(v2Ptr); + } + Blt_VectorUpdateClients(v2Ptr); + } + } else { + double norm; + + for (i = 0; i < vPtr->length; i++) { + norm = (vPtr->valueArr[i] - vPtr->min) / range; + Tcl_AppendElement(interp, Blt_Dtoa(interp, norm)); + } + } + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * NotifyOp -- + * + * Notify clients of vector. + * + * Results: + * A standard Tcl result. If any of the given vectors differ in size, + * TCL_ERROR is returned. Otherwise TCL_OK is returned and the + * vector data will contain merged values of the given vectors. + * + * x vector notify now + * x vector notify always + * x vector notify whenidle + * x vector notify update {} + * x vector notify delete {} + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +NotifyOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + char c; + int length; + + c = argv[2][0]; + length = strlen(argv[2]); + if ((c == 'a') && (length > 1) + && (strncmp(argv[2], "always", length) == 0)) { + vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK; + vPtr->notifyFlags |= NOTIFY_ALWAYS; + } else if ((c == 'n') && (length > 2) + && (strncmp(argv[2], "never", length) == 0)) { + vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK; + vPtr->notifyFlags |= NOTIFY_NEVER; + } else if ((c == 'w') && (length > 1) + && (strncmp(argv[2], "whenidle", length) == 0)) { + vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK; + vPtr->notifyFlags |= NOTIFY_WHENIDLE; + } else if ((c == 'n') && (length > 2) + && (strncmp(argv[2], "now", length) == 0)) { + /* How does this play when an update is pending? */ + Blt_VectorNotifyClients(vPtr); + } else if ((c == 'c') && (length > 1) + && (strncmp(argv[2], "cancel", length) == 0)) { + if (vPtr->notifyFlags & NOTIFY_PENDING) { + vPtr->notifyFlags &= ~NOTIFY_PENDING; + Tcl_CancelIdleCall(Blt_VectorNotifyClients, vPtr); + } + } else if ((c == 'p') && (length > 1) + && (strncmp(argv[2], "pending", length) == 0)) { + Blt_SetBooleanResult(interp, (vPtr->notifyFlags & NOTIFY_PENDING)); + } else { + Tcl_AppendResult(interp, "bad qualifier \"", argv[2], "\": should be \ +\"always\", \"never\", \"whenidle\", \"now\", \"cancel\", or \"pending\"", + (char *)NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * PopulateOp -- + * + * Creates or resizes a new vector based upon the density specified. + * + * Results: + * A standard Tcl result. If the density is invalid, TCL_ERROR + * is returned. Otherwise TCL_OK is returned. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +PopulateOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + VectorObject *v2Ptr; + int size, density; + int isNew; + register int i, j; + double slice, range; + register double *valuePtr; + int count; + + v2Ptr = Blt_VectorCreate(vPtr->dataPtr, argv[2], argv[2], argv[2], + &isNew); + if (v2Ptr == NULL) { + return TCL_ERROR; + } + if (vPtr->length == 0) { + return TCL_OK; /* Source vector is empty. */ + } + if (Tcl_GetInt(interp, argv[3], &density) != TCL_OK) { + return TCL_ERROR; + } + if (density < 1) { + Tcl_AppendResult(interp, "bad density \"", argv[3], "\"", (char *)NULL); + return TCL_ERROR; + } + size = (vPtr->length - 1) * (density + 1) + 1; + if (Blt_VectorChangeLength(v2Ptr, size) != TCL_OK) { + return TCL_ERROR; + } + count = 0; + valuePtr = v2Ptr->valueArr; + for (i = 0; i < (vPtr->length - 1); i++) { + range = vPtr->valueArr[i + 1] - vPtr->valueArr[i]; + slice = range / (double)(density + 1); + for (j = 0; j <= density; j++) { + *valuePtr = vPtr->valueArr[i] + (slice * (double)j); + valuePtr++; + count++; + } + } + count++; + *valuePtr = vPtr->valueArr[i]; + assert(count == v2Ptr->length); + if (!isNew) { + if (v2Ptr->flush) { + Blt_VectorFlushCache(v2Ptr); + } + Blt_VectorUpdateClients(v2Ptr); + } + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * RangeOp -- + * + * Returns a Tcl list of the range of vector values specified. + * + * Results: + * A standard Tcl result. If the given range is invalid, TCL_ERROR + * is returned. Otherwise TCL_OK is returned and interp->result + * will contain the list of values. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +RangeOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; /* Not used. */ + char **argv; +{ + int first, last; + register int i; + + if ((Blt_VectorGetIndex(interp, vPtr, argv[2], &first, INDEX_CHECK, + (Blt_VectorIndexProc **) NULL) != TCL_OK) || + (Blt_VectorGetIndex(interp, vPtr, argv[3], &last, INDEX_CHECK, + (Blt_VectorIndexProc **) NULL) != TCL_OK)) { + return TCL_ERROR; + } + if (first > last) { + /* Return the list reversed */ + for (i = last; i <= first; i++) { + Tcl_AppendElement(interp, Blt_Dtoa(interp, vPtr->valueArr[i])); + } + } else { + for (i = first; i <= last; i++) { + Tcl_AppendElement(interp, Blt_Dtoa(interp, vPtr->valueArr[i])); + } + } + return TCL_OK; +} + +/* + * ---------------------------------------------------------------------- + * + * InRange -- + * + * Determines if a value lies within a given range. + * + * The value is normalized and compared against the interval + * [0..1], where 0.0 is the minimum and 1.0 is the maximum. + * DBL_EPSILON is the smallest number that can be represented + * on the host machine, such that (1.0 + epsilon) != 1.0. + * + * Please note, min can't be greater than max. + * + * Results: + * If the value is within of the interval [min..max], 1 is + * returned; 0 otherwise. + * + * ---------------------------------------------------------------------- + */ +INLINE static int +InRange(value, min, max) + register double value, min, max; +{ + double range; + + range = max - min; + if (range < DBL_EPSILON) { + return (FABS(max - value) < DBL_EPSILON); + } else { + double norm; + + norm = (value - min) / range; + return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON)); + } +} + +enum NativeFormats { + FMT_UNKNOWN = -1, + FMT_UCHAR, FMT_CHAR, + FMT_USHORT, FMT_SHORT, + FMT_UINT, FMT_INT, + FMT_ULONG, FMT_LONG, + FMT_FLOAT, FMT_DOUBLE +}; + +/* + * ----------------------------------------------------------------------- + * + * GetBinaryFormat + * + * Translates a format string into a native type. Formats may be + * as follows. + * + * signed i1, i2, i4, i8 + * unsigned u1, u2, u4, u8 + * real r4, r8, r16 + * + * But there must be a corresponding native type. For example, + * this for reading 2-byte binary integers from an instrument and + * converting them to unsigned shorts or ints. + * + * ----------------------------------------------------------------------- + */ +static enum NativeFormats +GetBinaryFormat(interp, string, sizePtr) + Tcl_Interp *interp; + char *string; + int *sizePtr; +{ + char c; + + c = tolower(string[0]); + if (Tcl_GetInt(interp, string + 1, sizePtr) != TCL_OK) { + Tcl_AppendResult(interp, "unknown binary format \"", string, + "\": incorrect byte size", (char *)NULL); + return TCL_ERROR; + } + switch (c) { + case 'r': + if (*sizePtr == sizeof(double)) { + return FMT_DOUBLE; + } else if (*sizePtr == sizeof(float)) { + return FMT_FLOAT; + } + break; + + case 'i': + if (*sizePtr == sizeof(char)) { + return FMT_CHAR; + } else if (*sizePtr == sizeof(int)) { + return FMT_INT; + } else if (*sizePtr == sizeof(long)) { + return FMT_LONG; + } else if (*sizePtr == sizeof(short)) { + return FMT_SHORT; + } + break; + + case 'u': + if (*sizePtr == sizeof(unsigned char)) { + return FMT_UCHAR; + } else if (*sizePtr == sizeof(unsigned int)) { + return FMT_UINT; + } else if (*sizePtr == sizeof(unsigned long)) { + return FMT_ULONG; + } else if (*sizePtr == sizeof(unsigned short)) { + return FMT_USHORT; + } + break; + + default: + Tcl_AppendResult(interp, "unknown binary format \"", string, + "\": should be either i#, r#, u# (where # is size in bytes)", + (char *)NULL); + return FMT_UNKNOWN; + } + Tcl_AppendResult(interp, "can't handle format \"", string, "\"", + (char *)NULL); + return FMT_UNKNOWN; +} + +static int +CopyValues(vPtr, byteArr, fmt, size, length, swap, indexPtr) + VectorObject *vPtr; + char *byteArr; + enum NativeFormats fmt; + int size; + int length; + int swap; + int *indexPtr; +{ + register int i, n; + int newSize; + + if ((swap) && (size > 1)) { + int nBytes = size * length; + register unsigned char *p; + register int left, right; + + for (i = 0; i < nBytes; i += size) { + p = (unsigned char *)(byteArr + i); + for (left = 0, right = size - 1; left < right; left++, right--) { + p[left] ^= p[right]; + p[right] ^= p[left]; + p[left] ^= p[right]; + } + + } + } + newSize = *indexPtr + length; + if (newSize > vPtr->length) { + if (Blt_VectorChangeLength(vPtr, newSize) != TCL_OK) { + return TCL_ERROR; + } + } +#define CopyArrayToVector(vPtr, arr) \ + for (i = 0, n = *indexPtr; i < length; i++, n++) { \ + (vPtr)->valueArr[n] = (double)(arr)[i]; \ + } + + switch (fmt) { + case FMT_CHAR: + CopyArrayToVector(vPtr, (char *)byteArr); + break; + + case FMT_UCHAR: + CopyArrayToVector(vPtr, (unsigned char *)byteArr); + break; + + case FMT_INT: + CopyArrayToVector(vPtr, (int *)byteArr); + break; + + case FMT_UINT: + CopyArrayToVector(vPtr, (unsigned int *)byteArr); + break; + + case FMT_LONG: + CopyArrayToVector(vPtr, (long *)byteArr); + break; + + case FMT_ULONG: + CopyArrayToVector(vPtr, (unsigned long *)byteArr); + break; + + case FMT_SHORT: + CopyArrayToVector(vPtr, (short int *)byteArr); + break; + + case FMT_USHORT: + CopyArrayToVector(vPtr, (unsigned short int *)byteArr); + break; + + case FMT_FLOAT: + CopyArrayToVector(vPtr, (float *)byteArr); + break; + + case FMT_DOUBLE: + CopyArrayToVector(vPtr, (double *)byteArr); + break; + + case FMT_UNKNOWN: + break; + } + *indexPtr += length; + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * BinreadOp -- + * + * Reads binary values from a Tcl channel. Values are either appended + * to the end of the vector or placed at a given index (using the + * "-at" option), overwriting existing values. Data is read until EOF + * is found on the channel or a specified number of values are read. + * (note that this is not necessarily the same as the number of bytes). + * + * The following flags are supported: + * -swap Swap bytes + * -at index Start writing data at the index. + * -format fmt Specifies the format of the data. + * + * This binary reader was created by Harald Kirsch (kir@iitb.fhg.de). + * + * Results: + * Returns a standard Tcl result. The interpreter result will contain + * the number of values (not the number of bytes) read. + * + * Caveats: + * Channel reads must end on an element boundary. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +BinreadOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + char *byteArr; + enum NativeFormats fmt; + int size, length, mode; + Tcl_Channel channel; + int arraySize, bytesRead; + int count, total; + int first; + int swap; + register int i; + + channel = Tcl_GetChannel(interp, argv[2], &mode); + if (channel == NULL) { + return TCL_ERROR; + } + if ((mode & TCL_READABLE) == 0) { + Tcl_AppendResult(interp, "channel \"", argv[2], + "\" wasn't opened for reading", (char *)NULL); + return TCL_ERROR; + } + first = vPtr->length; + fmt = FMT_DOUBLE; + size = sizeof(double); + swap = FALSE; + count = 0; + + if ((argc > 3) && (argv[3][0] != '-')) { + long int value; + /* Get the number of values to read. */ + if (Tcl_ExprLong(interp, argv[3], &value) != TCL_OK) { + return TCL_ERROR; + } + if (value < 0) { + Tcl_AppendResult(interp, "count can't be negative", (char *)NULL); + return TCL_ERROR; + } + count = (int)value; + argc--, argv++; + } + /* Process any option-value pairs that remain. */ + for (i = 3; i < argc; i++) { + if (strcmp(argv[i], "-swap") == 0) { + swap = TRUE; + } else if (strcmp(argv[i], "-format") == 0) { + i += 1; + if (i >= argc) { + Tcl_AppendResult(interp, "missing arg after \"", argv[i - 1], + "\"", (char *)NULL); + return TCL_ERROR; + } + fmt = GetBinaryFormat(interp, argv[i], &size); + if (fmt == FMT_UNKNOWN) { + return TCL_ERROR; + } + } else if (strcmp(argv[i], "-at") == 0) { + i += 1; + if (i >= argc) { + Tcl_AppendResult(interp, "missing arg after \"", argv[i - 1], + "\"", (char *)NULL); + return TCL_ERROR; + } + if (Blt_VectorGetIndex(interp, vPtr, argv[i], &first, 0, + (Blt_VectorIndexProc **)NULL) != TCL_OK) { + return TCL_ERROR; + } + if (first > vPtr->length) { + Tcl_AppendResult(interp, "index \"", argv[i], + "\" is out of range", (char *)NULL); + return TCL_ERROR; + } + } + } + +#define BUFFER_SIZE 1024 + if (count == 0) { + arraySize = BUFFER_SIZE * size; + } else { + arraySize = count * size; + } + + byteArr = Blt_Malloc(arraySize); + assert(byteArr); + + /* FIXME: restore old channel translation later? */ + if (Tcl_SetChannelOption(interp, channel, "-translation", + "binary") != TCL_OK) { + return TCL_ERROR; + } + total = 0; + while (!Tcl_Eof(channel)) { + bytesRead = Tcl_Read(channel, byteArr, arraySize); + if (bytesRead < 0) { + Tcl_AppendResult(interp, "error reading channel: ", + Tcl_PosixError(interp), (char *)NULL); + return TCL_ERROR; + } + if ((bytesRead % size) != 0) { + Tcl_AppendResult(interp, "error reading channel: short read", + (char *)NULL); + return TCL_ERROR; + } + length = bytesRead / size; + if (CopyValues(vPtr, byteArr, fmt, size, length, swap, &first) + != TCL_OK) { + return TCL_ERROR; + } + total += length; + if (count > 0) { + break; + } + } + Blt_Free(byteArr); + + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + Blt_VectorUpdateClients(vPtr); + + /* Set the result as the number of values read. */ + Tcl_SetResult(interp, Blt_Itoa(total), TCL_VOLATILE); + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * SearchOp -- + * + * Searchs for a value in the vector. Returns the indices of all + * vector elements matching a particular value. + * + * Results: + * Always returns TCL_OK. interp->result will contain a list of + * the indices of array elements matching value. If no elements + * match, interp->result will contain the empty string. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +SearchOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; /* Not used. */ + char **argv; +{ + double min, max; + register int i; + int wantValue; + + wantValue = FALSE; + if ((argv[2][0] == '-') && (strcmp(argv[2], "-value") == 0)) { + wantValue = TRUE; + argv++, argc--; + } + if (Tcl_ExprDouble(interp, argv[2], &min) != TCL_OK) { + return TCL_ERROR; + } + max = min; + if ((argc > 3) && (Tcl_ExprDouble(interp, argv[3], &max) != TCL_OK)) { + return TCL_ERROR; + } + if ((min - max) >= DBL_EPSILON) { + return TCL_OK; /* Bogus range. Don't bother looking. */ + } + if (wantValue) { + for (i = 0; i < vPtr->length; i++) { + if (InRange(vPtr->valueArr[i], min, max)) { + Tcl_AppendElement(interp, Blt_Dtoa(interp, vPtr->valueArr[i])); + } + } + } else { + for (i = 0; i < vPtr->length; i++) { + if (InRange(vPtr->valueArr[i], min, max)) { + Tcl_AppendElement(interp, Blt_Itoa(i + vPtr->offset)); + } + } + } + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * OffsetOp -- + * + * Queries or sets the offset of the array index from the base + * address of the data array of values. + * + * Results: + * A standard Tcl result. If the source vector doesn't exist + * or the source list is not a valid list of numbers, TCL_ERROR + * returned. Otherwise TCL_OK is returned. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +OffsetOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; /* Not used. */ + char **argv; +{ + if (argc == 3) { + int newOffset; + + if (Tcl_GetInt(interp, argv[2], &newOffset) != TCL_OK) { + return TCL_ERROR; + } + vPtr->offset = newOffset; + } + Tcl_SetResult(interp, Blt_Itoa(vPtr->offset), TCL_VOLATILE); + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * RandomOp -- + * + * Generates random values for the length of the vector. + * + * Results: + * A standard Tcl result. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +RandomOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; /* Not used. */ + char **argv; +{ +#ifdef HAVE_DRAND48 + register int i; + + for (i = 0; i < vPtr->length; i++) { + vPtr->valueArr[i] = drand48(); + } +#endif /* HAVE_DRAND48 */ + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + Blt_VectorUpdateClients(vPtr); + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * SequenceOp -- + * + * Generates a sequence of values in the vector. + * + * Results: + * A standard Tcl result. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +SequenceOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; /* Not used. */ + char **argv; +{ + register int i; + double start, finish, step; + int fillVector; + int nSteps; + + if (Tcl_ExprDouble(interp, argv[2], &start) != TCL_OK) { + return TCL_ERROR; + } + fillVector = FALSE; + if ((argv[3][0] == 'e') && (strcmp(argv[3], "end") == 0)) { + fillVector = TRUE; + } else if (Tcl_ExprDouble(interp, argv[3], &finish) != TCL_OK) { + return TCL_ERROR; + } + step = 1.0; + if ((argc > 4) && (Tcl_ExprDouble(interp, argv[4], &step) != TCL_OK)) { + return TCL_ERROR; + } + if (fillVector) { + nSteps = vPtr->length; + } else { + nSteps = (int)((finish - start) / step) + 1; + } + if (nSteps > 0) { + if (Blt_VectorChangeLength(vPtr, nSteps) != TCL_OK) { + return TCL_ERROR; + } + for (i = 0; i < nSteps; i++) { + vPtr->valueArr[i] = start + (step * (double)i); + } + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + Blt_VectorUpdateClients(vPtr); + } + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * SetOp -- + * + * Sets the data of the vector object from a list of values. + * + * Results: + * A standard Tcl result. If the source vector doesn't exist + * or the source list is not a valid list of numbers, TCL_ERROR + * returned. Otherwise TCL_OK is returned. + * + * Side Effects: + * The vector data is reset. Clients of the vector are notified. + * Any cached array indices are flushed. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +SetOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; /* Not used. */ + char **argv; +{ + int result; + VectorObject *v2Ptr; + int nElem; + char **elemArr; + + /* The source can be either a list of expressions of another + * vector. */ + if (Tcl_SplitList(interp, argv[2], &nElem, &elemArr) != TCL_OK) { + return TCL_ERROR; + } + /* If there's only one element, see whether it's the name of a + * vector. Otherwise, treat it as a single numeric expression. */ + + if ((nElem == 1) && ((v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, + vPtr->dataPtr, argv[2], (char **)NULL, NS_SEARCH_BOTH)) != NULL)) { + if (vPtr == v2Ptr) { + VectorObject *tmpPtr; + + /* + * Source and destination vectors are the same. Copy the + * source first into a temporary vector to avoid memory + * overlaps. + */ + tmpPtr = Blt_VectorNew(vPtr->dataPtr); + result = Blt_VectorDuplicate(tmpPtr, v2Ptr); + if (result == TCL_OK) { + result = Blt_VectorDuplicate(vPtr, tmpPtr); + } + Blt_VectorFree(tmpPtr); + } else { + result = Blt_VectorDuplicate(vPtr, v2Ptr); + } + } else { + result = CopyList(vPtr, nElem, elemArr); + } + Blt_Free(elemArr); + + if (result == TCL_OK) { + /* + * The vector has changed; so flush the array indices (they're + * wrong now), find the new range of the data, and notify + * the vector's clients that it's been modified. + */ + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + Blt_VectorUpdateClients(vPtr); + } + return result; +} + +static VectorObject **sortVectorArr; /* Pointer to the array of values currently + * being sorted. */ +static int nSortVectors; +static int reverse; /* Indicates the ordering of the sort. If + * non-zero, the vectors are sorted in + * decreasing order */ + +static int +CompareVectors(a, b) + void *a; + void *b; +{ + double delta; + int i; + int sign; + register VectorObject *vPtr; + + sign = (reverse) ? -1 : 1; + for (i = 0; i < nSortVectors; i++) { + vPtr = sortVectorArr[i]; + delta = vPtr->valueArr[*(int *)a] - vPtr->valueArr[*(int *)b]; + if (delta < 0.0) { + return (-1 * sign); + } else if (delta > 0.0) { + return (1 * sign); + } + } + return 0; +} + +int * +Blt_VectorSortIndex(vPtrPtr, nVectors) + VectorObject **vPtrPtr; + int nVectors; +{ + int *indexArr; + register int i; + VectorObject *vPtr = *vPtrPtr; + + indexArr = Blt_Malloc(sizeof(int) * vPtr->length); + assert(indexArr); + for (i = 0; i < vPtr->length; i++) { + indexArr[i] = i; + } + sortVectorArr = vPtrPtr; + nSortVectors = nVectors; + qsort((char *)indexArr, vPtr->length, sizeof(int), + (QSortCompareProc *)CompareVectors); + return indexArr; +} + +static int * +SortVectors(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + VectorObject **vPtrArray, *v2Ptr; + int *iArr; + register int i; + + vPtrArray = Blt_Malloc(sizeof(VectorObject *) * (argc + 1)); + assert(vPtrArray); + vPtrArray[0] = vPtr; + iArr = NULL; + for (i = 0; i < argc; i++) { + if (Blt_VectorLookupName(vPtr->dataPtr, argv[i], &v2Ptr) != TCL_OK) { + goto error; + } + if (v2Ptr->length != vPtr->length) { + Tcl_AppendResult(interp, "vector \"", v2Ptr->name, + "\" is not the same size as \"", vPtr->name, "\"", + (char *)NULL); + goto error; + } + vPtrArray[i + 1] = v2Ptr; + } + iArr = Blt_VectorSortIndex(vPtrArray, argc + 1); + error: + Blt_Free(vPtrArray); + return iArr; +} + + +/* + * ----------------------------------------------------------------------- + * + * SortOp -- + * + * Sorts the vector object and any other vectors according to + * sorting order of the vector object. + * + * Results: + * A standard Tcl result. If any of the auxiliary vectors are + * a different size than the sorted vector object, TCL_ERROR is + * returned. Otherwise TCL_OK is returned. + * + * Side Effects: + * The vectors are sorted. + * + * ----------------------------------------------------------------------- + */ + +static int +SortOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + int *iArr; + double *mergeArr; + VectorObject *v2Ptr; + int refSize, nBytes; + int result; + register int i, n; + + reverse = FALSE; + if ((argc > 2) && (argv[2][0] == '-')) { + int length; + + length = strlen(argv[2]); + if ((length > 1) && (strncmp(argv[2], "-reverse", length) == 0)) { + reverse = TRUE; + } else { + Tcl_AppendResult(interp, "unknown flag \"", argv[2], + "\": should be \"-reverse\"", (char *)NULL); + return TCL_ERROR; + } + argc--, argv++; + } + if (argc > 2) { + iArr = SortVectors(vPtr, interp, argc - 2, argv + 2); + } else { + iArr = Blt_VectorSortIndex(&vPtr, 1); + } + if (iArr == NULL) { + return TCL_ERROR; + } + refSize = vPtr->length; + + /* + * Create an array to store a copy of the current values of the + * vector. We'll merge the values back into the vector based upon + * the indices found in the index array. + */ + nBytes = sizeof(double) * refSize; + mergeArr = Blt_Malloc(nBytes); + assert(mergeArr); + memcpy((char *)mergeArr, (char *)vPtr->valueArr, nBytes); + for (n = 0; n < refSize; n++) { + vPtr->valueArr[n] = mergeArr[iArr[n]]; + } + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + Blt_VectorUpdateClients(vPtr); + + /* Now sort any other vectors in the same fashion. The vectors + * must be the same size as the iArr though. */ + result = TCL_ERROR; + for (i = 2; i < argc; i++) { + if (Blt_VectorLookupName(vPtr->dataPtr, argv[i], &v2Ptr) != TCL_OK) { + goto error; + } + if (v2Ptr->length != refSize) { + Tcl_AppendResult(interp, "vector \"", v2Ptr->name, + "\" is not the same size as \"", vPtr->name, "\"", + (char *)NULL); + goto error; + } + memcpy((char *)mergeArr, (char *)v2Ptr->valueArr, nBytes); + for (n = 0; n < refSize; n++) { + v2Ptr->valueArr[n] = mergeArr[iArr[n]]; + } + Blt_VectorUpdateClients(v2Ptr); + if (v2Ptr->flush) { + Blt_VectorFlushCache(v2Ptr); + } + } + result = TCL_OK; + error: + Blt_Free(mergeArr); + Blt_Free(iArr); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * InstExprOp -- + * + * Computes the result of the expression which may be + * either a scalar (single value) or vector (list of values). + * + * Results: + * A standard Tcl result. + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +InstExprOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; + char **argv; +{ + if (Blt_ExprVector(interp, argv[2], (Blt_Vector *) vPtr) != TCL_OK) { + return TCL_ERROR; + } + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + Blt_VectorUpdateClients(vPtr); + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * ArithOp -- + * + * Results: + * A standard Tcl result. If the source vector doesn't exist + * or the source list is not a valid list of numbers, TCL_ERROR + * returned. Otherwise TCL_OK is returned. + * + * Side Effects: + * The vector data is reset. Clients of the vector are notified. + * Any cached array indices are flushed. + * + * ----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +ArithOp(vPtr, interp, argc, argv) + VectorObject *vPtr; + Tcl_Interp *interp; + int argc; /* Not used. */ + char **argv; +{ + register double value; + register int i; + VectorObject *v2Ptr; + + v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, argv[2], + (char **)NULL, NS_SEARCH_BOTH); + if (v2Ptr != NULL) { + register int j; + int length; + + length = v2Ptr->last - v2Ptr->first + 1; + if (length != vPtr->length) { + Tcl_AppendResult(interp, "vectors \"", argv[0], "\" and \"", + argv[2], "\" are not the same length", (char *)NULL); + return TCL_ERROR; + } + switch (argv[1][0]) { + case '*': + for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { + value = vPtr->valueArr[i] * v2Ptr->valueArr[j]; + Tcl_AppendElement(interp, Blt_Dtoa(interp, value)); + } + break; + + case '/': + for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { + value = vPtr->valueArr[i] / v2Ptr->valueArr[j]; + Tcl_AppendElement(interp, Blt_Dtoa(interp, value)); + } + break; + + case '-': + for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { + value = vPtr->valueArr[i] - v2Ptr->valueArr[j]; + Tcl_AppendElement(interp, Blt_Dtoa(interp, value)); + } + break; + + case '+': + for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { + value = vPtr->valueArr[i] + v2Ptr->valueArr[j]; + Tcl_AppendElement(interp, Blt_Dtoa(interp, value)); + } + break; + } + } else { + double scalar; + + if (Tcl_ExprDouble(interp, argv[2], &scalar) != TCL_OK) { + return TCL_ERROR; + } + switch (argv[1][0]) { + case '*': + for (i = 0; i < vPtr->length; i++) { + value = vPtr->valueArr[i] * scalar; + Tcl_AppendElement(interp, Blt_Dtoa(interp, value)); + } + break; + + case '/': + for (i = 0; i < vPtr->length; i++) { + value = vPtr->valueArr[i] / scalar; + Tcl_AppendElement(interp, Blt_Dtoa(interp, value)); + } + break; + + case '-': + for (i = 0; i < vPtr->length; i++) { + value = vPtr->valueArr[i] - scalar; + Tcl_AppendElement(interp, Blt_Dtoa(interp, value)); + } + break; + + case '+': + for (i = 0; i < vPtr->length; i++) { + value = vPtr->valueArr[i] + scalar; + Tcl_AppendElement(interp, Blt_Dtoa(interp, value)); + } + break; + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * VectorInstCmd -- + * + * Parses and invokes the appropriate vector instance command + * option. + * + * Results: + * A standard Tcl result. + * + *---------------------------------------------------------------------- + */ +static Blt_OpSpec vectorInstOps[] = +{ + {"*", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/ + {"+", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/ + {"-", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/ + {"/", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/ + {"append", 1, (Blt_Op)AppendOp, 3, 0, "item ?item...?",}, + {"binread", 1, (Blt_Op)BinreadOp, 3, 0, "channel ?numValues? ?flags?",}, + {"clear", 1, (Blt_Op)ClearOp, 2, 2, "",}, + {"delete", 2, (Blt_Op)DeleteOp, 2, 0, "index ?index...?",}, + {"dup", 2, (Blt_Op)DupOp, 3, 0, "vecName",}, + {"expr", 1, (Blt_Op)InstExprOp, 3, 3, "expression",}, + {"index", 1, (Blt_Op)IndexOp, 3, 4, "index ?value?",}, + {"length", 1, (Blt_Op)LengthOp, 2, 3, "?newSize?",}, + {"merge", 1, (Blt_Op)MergeOp, 3, 0, "vecName ?vecName...?",}, + {"normalize", 3, (Blt_Op)NormalizeOp, 2, 3, "?vecName?",}, /*Deprecated*/ + {"notify", 3, (Blt_Op)NotifyOp, 3, 3, "keyword",}, + {"offset", 2, (Blt_Op)OffsetOp, 2, 3, "?offset?",}, + {"populate", 1, (Blt_Op)PopulateOp, 4, 4, "vecName density",}, + {"random", 4, (Blt_Op)RandomOp, 2, 2, "",}, /*Deprecated*/ + {"range", 4, (Blt_Op)RangeOp, 4, 4, "first last",}, + {"search", 3, (Blt_Op)SearchOp, 3, 4, "?-value? value ?value?",}, + {"seq", 3, (Blt_Op)SequenceOp, 4, 5, "start end ?step?",}, + {"set", 3, (Blt_Op)SetOp, 3, 3, "list",}, + {"sort", 2, (Blt_Op)SortOp, 2, 0, "?-reverse? ?vecName...?",}, + {"variable", 1, (Blt_Op)MapOp, 2, 3, "?varName?",}, +}; + +static int nInstOps = sizeof(vectorInstOps) / sizeof(Blt_OpSpec); + +int +Blt_VectorInstCmd(clientData, interp, argc, argv) + ClientData clientData; + Tcl_Interp *interp; + int argc; + char **argv; +{ + Blt_Op proc; + VectorObject *vPtr = clientData; + + vPtr->first = 0; + vPtr->last = vPtr->length - 1; + proc = Blt_GetOp(interp, nInstOps, vectorInstOps, BLT_OP_ARG1, argc, argv, + 0); + if (proc == NULL) { + return TCL_ERROR; + } + return (*proc) (vPtr, interp, argc, argv); +} + + +/* + * ---------------------------------------------------------------------- + * + * Blt_VectorVarTrace -- + * + * Results: + * Returns NULL on success. Only called from a variable trace. + * + * Side effects: + * + * ---------------------------------------------------------------------- + */ +char * +Blt_VectorVarTrace(clientData, interp, part1, part2, flags) + ClientData clientData; /* File output information. */ + Tcl_Interp *interp; + char *part1, *part2; + int flags; +{ + VectorObject *vPtr = clientData; + char string[TCL_DOUBLE_SPACE + 1]; +#define MAX_ERR_MSG 1023 + static char message[MAX_ERR_MSG + 1]; + Blt_VectorIndexProc *indexProc; + int varFlags; + int first, last; + + if (part2 == NULL) { + if (flags & TCL_TRACE_UNSETS) { + Blt_Free(vPtr->arrayName); + vPtr->arrayName = NULL; + vPtr->varNsPtr = NULL; + if (vPtr->freeOnUnset) { + Blt_VectorFree(vPtr); + } + } + return NULL; + } + if (Blt_VectorGetIndexRange(interp, vPtr, part2, INDEX_ALL_FLAGS, + &indexProc) != TCL_OK) { + goto error; + } + first = vPtr->first, last = vPtr->last; + varFlags = TCL_LEAVE_ERR_MSG | (TCL_GLOBAL_ONLY & flags); + if (flags & TCL_TRACE_WRITES) { + double value; + char *newValue; + + if (first == SPECIAL_INDEX) { /* Tried to set "min" or "max" */ + return "read-only index"; + } + newValue = Tcl_GetVar2(interp, part1, part2, varFlags); + if (newValue == NULL) { + goto error; + } + if (Tcl_ExprDouble(interp, newValue, &value) != TCL_OK) { + if ((last == first) && (first >= 0)) { + /* Single numeric index. Reset the array element to + * its old value on errors */ + Tcl_PrintDouble(interp, vPtr->valueArr[first], string); + Tcl_SetVar2(interp, part1, part2, string, varFlags); + } + goto error; + } + if (first == vPtr->length) { + if (Blt_VectorChangeLength(vPtr, vPtr->length + 1) != TCL_OK) { + return "error resizing vector"; + } + } + /* Set possibly an entire range of values */ + ReplicateValue(vPtr, first, last, value); + } else if (flags & TCL_TRACE_READS) { + double value; + + if (vPtr->length == 0) { + if (Tcl_SetVar2(interp, part1, part2, "", varFlags) == NULL) { + goto error; + } + return NULL; + } + if (first == vPtr->length) { + return "write-only index"; + } + if (first == last) { + if (first >= 0) { + value = vPtr->valueArr[first]; + } else { + vPtr->first = 0, vPtr->last = vPtr->length - 1; + value = (*indexProc) ((Blt_Vector *) vPtr); + } + Tcl_PrintDouble(interp, value, string); + if (Tcl_SetVar2(interp, part1, part2, string, varFlags) + == NULL) { + goto error; + } + } else { + Tcl_DString dString; + char *result; + + Tcl_DStringInit(&dString); + GetValues(vPtr, first, last, &dString); + result = Tcl_SetVar2(interp, part1, part2, + Tcl_DStringValue(&dString), varFlags); + Tcl_DStringFree(&dString); + if (result == NULL) { + goto error; + } + } + } else if (flags & TCL_TRACE_UNSETS) { + register int i, j; + + if ((first == vPtr->length) || (first == SPECIAL_INDEX)) { + return "special vector index"; + } + /* + * Collapse the vector from the point of the first unset element. + * Also flush any array variable entries so that the shift is + * reflected when the array variable is read. + */ + for (i = first, j = last + 1; j < vPtr->length; i++, j++) { + vPtr->valueArr[i] = vPtr->valueArr[j]; + } + vPtr->length -= ((last - first) + 1); + if (vPtr->flush) { + Blt_VectorFlushCache(vPtr); + } + } else { + return "unknown variable trace flag"; + } + if (flags & (TCL_TRACE_UNSETS | TCL_TRACE_WRITES)) { + Blt_VectorUpdateClients(vPtr); + } + Tcl_ResetResult(interp); + return NULL; + + error: + strncpy(message, Tcl_GetStringResult(interp), MAX_ERR_MSG); + message[MAX_ERR_MSG] = '\0'; + return message; +} + +#endif /* TCL_MAJOR_VERSION == 7 */ |