/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include #include #include static Array MakeSequence(Array start, Array end, int *n, Array delta, char *method); static Array MakeLinearList(Array start, Array end, int *n, Array delta); static Array MakeLogList(Array start, Array end, int *n, Array delta); static Array MakeLog10List(Array start, Array end, int *n, Array delta); static Array MakeExponentialList(Array start, Array end, int *n, Array delta); static Array MakeGeometricList(Array start, Array end, int *n, Array delta); static Array MakeFibonacciList(Array start, Array end, int *n); static void lowercase(char *in, char *out); static int IsZero(Array a); static int IsEqual(Array a, Array b); static void Negate(Array a); static int IsNegative(Array a); static char *TypeName(Type t); /* the module is intended to make generating lists easy. * if any 3 of the first 4 parms are given, this generates the * missing parm and makes a list. * * if the last parm is a string, this generates lists with uneven spacings * like exponential, fibbonaci, etc */ #define icheck(obj, label) \ if (obj) { \ int n; \ \ if (DXGetObjectClass(obj) != CLASS_ARRAY) { \ DXSetError(ERROR_BAD_PARAMETER, "#10380", label); \ goto error; \ } \ \ if (!DXGetArrayInfo((Array)obj, &n, NULL, NULL, NULL, NULL)) \ goto error; \ \ if (n != 1) { \ DXSetError(ERROR_BAD_PARAMETER, "#10381", label); \ goto error; \ } \ } int m_Enumerate(Object *in, Object *out) { int i; int *ip = NULL; char *cp = NULL; out[0] = NULL; icheck(in[0], "start"); icheck(in[1], "end"); if (in[2]) { if (!DXExtractInteger(in[2], &i) || (i <= 0)) { DXSetError(ERROR_BAD_PARAMETER, "#10020", "count"); goto error; } ip = &i; } icheck(in[3], "delta"); if (in[4] && !DXExtractString(in[4], &cp)) { DXSetError(ERROR_BAD_PARAMETER, "#10200", "method"); goto error; } out[0] = (Object)MakeSequence((Array)in[0], (Array)in[1], ip, (Array)in[3], cp); error: return out[0] ? OK : ERROR; } /* * internal entry point; generate a single array given 3 of the 4 * start, end, nitems, delta using method for spacing. */ static Array MakeSequence(Array start, Array end, int *n, Array delta, char *method) { char *lmethod = NULL; Array output = NULL; if (!method) return MakeLinearList(start, end, n, delta); lmethod = DXAllocateLocalZero(strlen(method)+1); if (!lmethod) return NULL; lowercase(method, lmethod); if (!strcmp(lmethod, "linear")) { output = MakeLinearList(start, end, n, delta); goto done; } if (!strcmp(lmethod, "log")) { output = MakeLogList(start, end, n, delta); goto done; } if (!strcmp(lmethod, "log10")) { output = MakeLog10List(start, end, n, delta); goto done; } if (!strcmp(lmethod, "exponential")) { output = MakeExponentialList(start, end, n, delta); goto done; } if (!strcmp(lmethod, "geometric")) { output = MakeGeometricList(start, end, n, delta); goto done; } if (!strcmp(lmethod, "fibonacci")) { if (delta) DXWarning("ignoring `delta' for fibonacci method"); output = MakeFibonacciList(start, end, n); goto done; } /* put other methods here and in the error message list below */ #if 0 /* eventually */ DXSetError(ERROR_BAD_PARAMETER, "#10211", "method", "`linear', `log', `log10', `exponential', `geometric' or `fibonacci'"); #else DXSetError(ERROR_BAD_PARAMETER, "#10370", "method", "`linear'"); #endif done: DXFree((Pointer)lmethod); return output; } #define MAXSHAPE 100 #define INCVOID(d, n) (void *)((byte *)d + n) #define MAXCOMPINPUTS 22 /* item[n] = start + n * delta */ static Array MakeLinearList(Array start, Array end, int *n, Array delta) { int i, j; Array a_start = NULL, a_end = NULL, a_delta = NULL; int delstart = 0, delend = 0, deldelta = 0; Array alist[3]; Array output = NULL; int count = 1; int bytes; Type t; Category c; int rank; int shape[MAXSHAPE]; int nitems; int i_start, i_end, i_delta; float f_start, f_end, f_delta, f_count; Pointer dp; int *ip; Object in[MAXCOMPINPUTS]; /* hardcoded in compute */ Object out; String compstr = NULL; char cbuf[64]; Error rc; /* do this or die in compute */ for (i=0; i= i_start && i_delta > 0) || (i_end < i_start && i_delta < 0)) count = (int)(((double)i_end-i_start) / (double)i_delta) +1; else { if (i_delta < 0) DXSetError(ERROR_BAD_PARAMETER, "delta must be positive if start is less than end"); else DXSetError(ERROR_BAD_PARAMETER, "delta must be negative if end is less than start"); goto error; } } } output = (Array)DXNewRegularArray(TYPE_INT, 1, count, (Pointer)&i_start, (Pointer)&i_delta); } /* for float, scalar lists */ if (t == TYPE_FLOAT) { if (!start) { f_end = *(float *)DXGetArrayData(a_end); f_delta = *(float *)DXGetArrayData(a_delta); f_start = f_end - ((count - 1.0) * f_delta); } if (!end) { f_start = *(float *)DXGetArrayData(a_start); f_delta = *(float *)DXGetArrayData(a_delta); f_end = f_start + ((count - 1.0) * f_delta); } if (!delta) { /* if count == 1, generate a zero of the right type. otherwise * divide to figure out the right delta to make count-1 steps * between start and end. it's count-1 because if you want * N numbers between start and end, you have N-1 increments. */ f_start = *(float *)DXGetArrayData(a_start); f_end = *(float *)DXGetArrayData(a_end); if (count == 1) f_delta = 0.0; else f_delta = (f_end - f_start) / (count - 1.0); /* try to catch the case where delta ends up being 0 (like * because the inputs are int and the count is larger than * the difference between start and end). allow it to be zero * only if start == end; i suppose if you ask for 10 things * where start == end you should be able to get them. */ if (f_delta == 0.0 && f_start != f_end) { DXSetError(ERROR_BAD_PARAMETER, "count too large to generate list between start and end"); goto error; } } /* if all three arrays are there, count must be missing */ if (i == 3) { f_start = *(float *)DXGetArrayData(a_start); f_end = *(float *)DXGetArrayData(a_end); f_delta = *(float *)DXGetArrayData(a_delta); if (f_delta == 0.0) count == 1; else { if ((f_end >= f_start && f_delta > 0) || (f_end < f_start && f_delta < 0)) { /* the intermediate float variable below is to minimize * float round-off error. if delta is 0.1 and you * ask for a list between 0 and 1, it does the math in * double, the delta used is actually 0.10000001, and * you get counts = 10.9999999 instead of 11. when * converted directly to int it becomes just 10 and your * list ends at 0.9 instead of 1. * math in base 2 has some problems. */ f_count = ((f_end - f_start) / f_delta) +1; count = (int)f_count; } else { if (f_delta < 0) DXSetError(ERROR_BAD_PARAMETER, "delta must be positive if start is less than end"); else DXSetError(ERROR_BAD_PARAMETER, "delta must be negative if end is less than start"); goto error; } } } output = (Array)DXNewRegularArray(TYPE_FLOAT, 1, count, (Pointer)&f_start, (Pointer)&f_delta); } DXDelete((Object)a_start); DXDelete((Object)a_end); DXDelete((Object)a_delta); /* return Array */ return output; /* input is a vector, or a data type different from int or float. * use compute so this code doesn't have to be replicated for each * different shape and type. */ complicated: nitems = 1; for (j=0; j= $0.%d) && ($2.%d > 0) || " " ($1.%d < $0.%d) && ($2.%d < 0)) ? " " int(float($1.%d - $0.%d) / float($2.%d)) + 1 : " " -1 ) ", j, j, j, j, j, j, j, j, j, j); compstr = DXNewString(tbuf); if (!compstr) goto error; in[0] = (Object)compstr; in[1] = (Object)start; in[2] = (Object)end; in[3] = (Object)delta; DXReference((Object)compstr); rc = m_Compute(in, &out); DXDelete((Object)compstr); compstr = NULL; if (rc == ERROR) goto error; if (!DXExtractInteger(out, &count)) { DXSetError(ERROR_BAD_PARAMETER, "can't compute number of items"); goto error; } DXDelete((Object)out); if (count == 0) continue; if (count < 0) { if (IsNegative(delta)) DXSetError(ERROR_BAD_PARAMETER, "delta must be positive if start is less than end"); else DXSetError(ERROR_BAD_PARAMETER, "delta must be negative if end is less than start"); goto error; } if (firsttime) { lastcount = count; firsttime = 0; } else { if (count != lastcount) { DXSetError(ERROR_BAD_PARAMETER, "inconsistent number of items required by inputs"); goto error; } } } } /* now have 4 consistant values - once again make sure they are * converted into an identical format. */ a_start = DXArrayConvertV(start, t, c, rank, shape); a_end = DXArrayConvertV(end, t, c, rank, shape); a_delta = DXArrayConvertV(delta, t, c, rank, shape); /* make empty array with n items */ output = DXNewArrayV(t, c, rank, shape); if (!output) goto error; if (!DXAddArrayData(output, 0, count, NULL)) goto error; dp = DXGetArrayData(output); if (!dp) goto error; /* foreach n */ /* call compute to add delta */ /* memcpy to right offset in array */ /* end */ bytes = DXGetItemSize(output); sprintf(cbuf, "%s($0 + ($1 * $2))", TypeName(t)); compstr = DXNewString(cbuf); if (!compstr) goto error; in[0] = (Object)compstr; in[1] = (Object)a_start; in[3] = (Object)a_delta; in[2] = (Object)DXNewArray(TYPE_INT, CATEGORY_REAL, 0); if (!in[2]) goto error; if (!DXAddArrayData((Array)in[2], 0, 1, NULL)) goto error; ip = (int *)DXGetArrayData((Array)in[2]); DXReference((Object)compstr); DXReference(in[2]); for (i=0; i= 0 */ static int IsNegative(Array a) { Type t; Pointer value; if (!DXGetArrayInfo(a, NULL, &t, NULL, NULL, NULL)) return; value = DXGetArrayData(a); if (!value) return; switch (t) { case TYPE_BYTE: return (*(byte *)value < (byte)0); case TYPE_SHORT: return (*(short *)value < (short)0); case TYPE_INT: return (*(int *)value < 0); case TYPE_FLOAT: return (*(float *)value < 0.0); case TYPE_DOUBLE: return (*(double *)value < 0.0); case TYPE_UBYTE: case TYPE_USHORT: case TYPE_UINT: default: return 0; } /* NOT REACHED */ } static char *TypeName(Type t) { switch(t) { case TYPE_UBYTE: return "ubyte"; case TYPE_BYTE: return "byte"; case TYPE_USHORT: return "ushort"; case TYPE_SHORT: return "short"; case TYPE_UINT: return "uint"; case TYPE_INT: return "int"; /* case TYPE_UHYPER: return "uhyper"; */ case TYPE_HYPER: return "hyper"; case TYPE_FLOAT: return "float"; case TYPE_DOUBLE: return "double"; case TYPE_STRING: return "string"; default: return "unknown"; } /* notreached */ }