1 /*
2 * $Id: std1.c,v 1.4 2010-10-12 01:42:23 dhmunro Exp $
3 * More Yorick built-in functions declared in std.i
4 *
5 * See std.i for documentation on the functions defined here.
6 */
7 /* Copyright (c) 2005, The Regents of the University of California.
8 * All rights reserved.
9 * This file is part of yorick (http://yorick.sourceforge.net).
10 * Read the accompanying LICENSE file for details.
11 */
12
13 #include "ydata.h"
14 #include "pstdlib.h"
15 #include "play.h"
16 #include <string.h>
17 /* ANSI C time.h for Y_timestamp */
18 #include <time.h>
19
20 extern BuiltIn Y_indgen, Y_span, Y_digitize, Y_interp, Y_integ, Y_sort,
21 Y_transpose, Y_grow, Y__, Y_timestamp, Y_timer,
22 Y_random, Y_random_seed, Y_merge, Y_histogram, Y_poly, Y_noop;
23
24 /*--------------------------------------------------------------------------*/
25
26 extern void PopToD(Symbol *s);
27 extern DataBlock *ForceToDB(Symbol *s);
28
29 /* Intended for use by the print() and grow() functions -- dangerous
30 because it zeroes the contents of the source array to avoid
31 having to deal with pointers. */
32 extern Array *GrowArray(Array *array, long extra); /* ydata.c */
33
34 static long hunt(double *x, long n, double xp, long ip);
35
36 /*--------------------------------------------------------------------------*/
37
Y_indgen(int nArgs)38 void Y_indgen(int nArgs)
39 {
40 long number, origin, stride, i;
41 Array *array;
42 Dimension *tmp;
43 Operand op;
44 if (nArgs != 1) YError("indgen takes exactly one argument");
45
46 sp->ops->FormOperand(sp, &op);
47 if (op.ops==&rangeOps) {
48 Range *range= op.value;
49 if (range->rf || range->nilFlags)
50 YError("range function and/or nil range component in indgen");
51 origin= range->min;
52 stride= range->inc;
53 if (stride>0) number= (range->max-origin)/stride;
54 else number= (origin-range->max)/(-stride);
55 number++; /* number of footprints, not number of strides */
56 } else if (op.ops->promoteID<=T_LONG && !op.type.dims) {
57 op.ops->ToLong(&op);
58 number= *(long *)op.value;
59 origin= 1L;
60 stride= 1;
61 } else {
62 YError("indgen argument must be range or scalar integer");
63 return;
64 }
65
66 if (number>0) {
67 tmp= tmpDims;
68 tmpDims= 0;
69 FreeDimension(tmp);
70 tmpDims= NewDimension(number, 1L, (Dimension *)0);
71 array= PushDataBlock(NewArray(&longStruct, tmpDims));
72
73 for (i=0 ; i<number ; i++) {
74 array->value.l[i]= origin;
75 origin+= stride;
76 }
77
78 } else {
79 /* indgen(0) returns default origin */
80 PushLongValue(1L);
81 }
82 }
83
Y_span(int nArgs)84 void Y_span(int nArgs)
85 {
86 long which, nDims, number, nfast, i, j, k, kx;
87 double rnumber, dp, *p0, *p1, *p;
88 Array *array;
89 Dimension *tmp;
90 Operand op0, op1;
91 if (nArgs!=3 && nArgs!=4)
92 YError("span takes exactly three or four arguments");
93
94 if (nArgs==4) { which= YGetInteger(sp)-1; Drop(1); }
95 else which= 0; /* use 0-origin which here */
96
97 number= YGetInteger(sp);
98 if (number<1) YError("3rd argument to span function must be >0");
99 Drop(1);
100
101 sp->ops->FormOperand(sp, &op1);
102 (sp-1)->ops->FormOperand(sp-1, &op0);
103 op1.ops->ToDouble(&op1);
104 op0.ops->ToDouble(&op0);
105 if (BinaryConform(&op1, &op0) & 4)
106 YError("start and stop not conformable in span function");
107
108 tmp= tmpDims;
109 tmpDims= 0;
110 FreeDimension(tmp);
111
112 /* compute dimensions of result -- nfast-by-number-by-(slow),
113 where nfast are the first which indices of op0 (or op1),
114 and (slow) are the rest */
115 nDims= CountDims(op0.type.dims);
116 if (which<0) which= nDims+1+which; /* handle which<0 like array index<0 */
117 if (which>8 || nDims-which>8)
118 YError("the 4th argument to span is unreasonably large");
119 while (which<0) {
120 tmpDims= NewDimension(1L, 1L, tmpDims);
121 which++;
122 }
123 if (which>=nDims) {
124 /* can just tack new element of dimension list onto old */
125 tmpDims= Ref(op0.type.dims);
126 while (which>nDims) {
127 tmpDims= NewDimension(1L, 1L, tmpDims);
128 which--;
129 }
130 tmpDims= NewDimension(number, 1L, tmpDims);
131 nfast= op0.type.number;
132 } else {
133 /* make a fresh copy of the index list, then insert new element */
134 Dimension *prev;
135
136 which= nDims-which; /* guaranteed this is >0 */
137 tmpDims= tmp= CopyDims(op0.type.dims, tmpDims, 1);
138 do {
139 prev= tmp;
140 tmp= tmp->next;
141 } while (--which);
142 prev->next= NewDimension(number, 1L, tmp);
143 nfast= TotalNumber(tmp);
144 }
145
146 /* create result array and fill it */
147 array= PushDataBlock(NewArray(&doubleStruct, tmpDims));
148 p= array->value.d;
149 p0= op0.value;
150 p1= op1.value;
151 rnumber= 1.0/(double)(number-1);
152 kx= array->type.number - nfast;
153 for (i=0 ; i<op0.type.number ; i+=nfast) {
154 for (j=0 ; j<nfast ; j++) {
155 dp= (p1[i+j]-p0[i+j])*rnumber;
156 k= j+number*i;
157 p[k]= p0[i+j];
158 for (k+=nfast ; k<kx ; k+=nfast) p[k]= p[k-nfast]+dp;
159 p[k]= p1[i+j]; /* do after loop to assure exact equality */
160 }
161 }
162 }
163
hunt(double * x,long n,double xp,long ip)164 static long hunt(double *x, long n, double xp, long ip)
165 {
166 /* Based on the hunt routine given in Numerical Recipes (Press, et al.,
167 Cambridge University Press, 1988), section 3.4.
168
169 Here, x[n] is a monotone array and, if xp lies in the interval
170 from x[0] to x[n-1], then
171 x[h-1] <= xp < x[h] (h is the value returned by hunt), or
172 x[h-1] >= xp > x[h], as x is ascending or descending
173 The value 0 or n will be returned if xp lies outside the interval.
174 */
175 int ascend= x[n-1]>x[0];
176 long jl, ju;
177
178 if (ip<1 || ip>n-1) {
179 /* caller has declined to make an initial guess, so fall back to
180 garden variety bisection method */
181 if ((xp>=x[n-1]) == ascend) return n;
182 if ((xp<x[0]) == ascend) return 0;
183 jl= 0;
184 ju= n-1;
185
186 } else {
187 /* search from initial guess ip in ever increasing steps to bracket xp */
188 int inc= 1;
189 jl= ip;
190 if ((xp>=x[ip]) == ascend) { /* search toward larger index values */
191 if (ip==n-1) return n;
192 jl= ip;
193 ju= ip+inc;
194 while ((xp>=x[ju]) == ascend) {
195 jl= ju;
196 inc+= inc;
197 ju+= inc;
198 if (ju>=n) {
199 if ((xp>=x[n-1]) == ascend) return n;
200 ju= n;
201 break;
202 }
203 }
204 } else { /* search toward smaller index values */
205 if (ip==0) return 0;
206 ju= ip;
207 jl= ip-inc;
208 while ((xp<x[jl]) == ascend) {
209 ju= jl;
210 inc+= inc;
211 jl-= inc;
212 if (jl<0) {
213 if ((xp<x[0]) == ascend) return 0;
214 jl= 0;
215 break;
216 }
217 }
218 }
219 }
220
221 /* have x[jl]<=xp<x[ju] for ascend, x[jl]>=xp>x[ju] for !ascend */
222 while (ju-jl > 1) {
223 ip= (jl+ju)>>1;
224 if ((xp>=x[ip]) == ascend) jl= ip;
225 else ju= ip;
226 }
227
228 return ju;
229 }
230
Y_digitize(int nArgs)231 void Y_digitize(int nArgs)
232 {
233 long number, origin, nbins, i, ip;
234 double *x, *bins;
235 Dimension *dimsx, *dimsb;
236 long *ibin;
237 if (nArgs!=2) YError("digitize takes exactly two arguments");
238
239 bins= YGet_D(sp, 0, &dimsb);
240 x= YGet_D(sp-1, 0, &dimsx);
241
242 if (!dimsb || dimsb->number<2 || dimsb->next)
243 YError("2nd argument to digitize must be 1D with >=2 elements");
244 nbins= dimsb->number;
245 origin= dimsb->origin;
246 number= TotalNumber(dimsx);
247
248 if (dimsx) {
249 Array *array= PushDataBlock(NewArray(&longStruct, dimsx));
250 ibin= array->value.l;
251 } else {
252 PushLongValue(0L);
253 ibin= &sp->value.l;
254 }
255 ip= 0;
256 for (i=0 ; i<number ; i++)
257 ibin[i]= ip= origin+hunt(bins, nbins, x[i], ip);
258 }
259
Y_interp(int nArgs)260 void Y_interp(int nArgs)
261 {
262 long which, nDims, number, nfast;
263 long i, j, k, l, m, js, ms, ip, ipy;
264 double *y, *x, *xp, *yp, c0, c1;
265 Array *array;
266 Dimension *tmp;
267 Operand opy, opx, opxp;
268 if (nArgs!=3 && nArgs!=4)
269 YError("interp takes exactly three or four arguments");
270
271 if (nArgs==4) { which= YGetInteger(sp)-1; Drop(1); }
272 else which= 0; /* use 0-origin which here */
273
274 sp->ops->FormOperand(sp, &opxp);
275 (sp-1)->ops->FormOperand(sp-1, &opx);
276 (sp-2)->ops->FormOperand(sp-2, &opy);
277 opxp.ops->ToDouble(&opxp);
278 opx.ops->ToDouble(&opx);
279 opy.ops->ToDouble(&opy);
280
281 tmp= tmpDims;
282 tmpDims= 0;
283 FreeDimension(tmp);
284
285 /* compute dimensions of y array -- nfast-by-number-by-(slow), where
286 nfast are the first which dimensions of y, number is the length of
287 the dimension to interpolate on, and (slow) are the rest */
288 nDims= CountDims(opy.type.dims);
289 if (which<0) which= nDims+which; /* handle which<0 like array index<0 */
290 if (which>=nDims || which<0) YError("bad 4th argument to interp");
291 i= nDims-1-which;
292 tmp= opy.type.dims;
293 while (i) { tmp= tmp->next; i--; }
294 number= tmp? tmp->number : 1;
295 if (number<2) YError("bad dimension (length 1) in interp");
296 nfast= TotalNumber(tmp->next);
297 if (opx.type.number!=number || opx.type.dims->next)
298 YError("dimension of x does not match target dimension of y in interp");
299
300 if (which==nDims-1) {
301 /* can just tack new element(s) of dimension list onto old */
302 tmpDims= Ref(opy.type.dims->next);
303 tmpDims= CopyDims(opxp.type.dims, tmpDims, 1);
304 } else {
305 /* make a fresh copy of the index list and find where to insert new */
306 Dimension *prev, *tmpprev;
307 i= nDims-which; /* guaranteed >1 AND opy is at least 2D */
308 tmpDims= prev= CopyDims(opy.type.dims, tmpDims, 1);
309 tmp= prev->next; i--;
310 tmp= tmp->next; i--; /* prev is two behind tmp */
311 while (i) { prev= prev->next; tmp= tmp->next; i--; }
312 tmpprev= prev->next;
313 prev->next= 0; /* cut off tail of dimension list */
314 tmpprev->next= 0; /* this pointed to tmp */
315 FreeDimension(tmpprev);
316 prev->next= CopyDims(opxp.type.dims, tmp, 1);
317 }
318
319 /* create result array */
320 array= PushDataBlock(NewArray(&doubleStruct, tmpDims));
321 yp= array->value.d;
322 y= opy.value;
323 x= opx.value;
324 xp= opxp.value;
325
326 /* The problem is as follows:
327 For each value of the faster indices, and each value of the slower
328 indices of y, and for each value of xp, find the value of yp
329 corresponding to xp. Note that for each interpolation, the y
330 vector has stride nfast, while the x vector always has stride 0.
331 Note also that ALL of the yp for a given xp should be computed
332 once xp is found. */
333 js= nfast*number;
334 ms= nfast*opxp.type.number;
335 c0= c1= 0.0;
336 ip= 0; /* hunt takes this as no guess on 1st pass */
337 for (i=l=0 ; i<opxp.type.number ; i++, l+=nfast) {
338 ip= hunt(x, number, xp[i], ip);
339 ipy= ip*nfast;
340 if (ip>=1 && ip<number) {
341 c0= (x[ip]-xp[i])/(x[ip]-x[ip-1]);
342 c1= 1.0-c0;
343 }
344 for (j=m=0 ; j<opy.type.number ; j+=js, m+=ms) {
345 for (k=0 ; k<nfast ; k++) {
346 if (ip<1) { /* point below minimum */
347 yp[k+l+m]= y[k+j];
348 } else if (ip<number) { /* point in range */
349 yp[k+l+m]= c0*y[k+(ipy-nfast)+j]+c1*y[k+ipy+j];
350 } else { /* point above maximum */
351 yp[k+l+m]= y[k+(ipy-nfast)+j];
352 }
353 }
354 }
355 }
356
357 PopToD(sp-4);
358 Drop(3);
359 }
360
Y_integ(int nArgs)361 void Y_integ(int nArgs)
362 {
363 long which, nDims, number, nfast;
364 long i, j, k, l, m, js, ms, ip, ipy;
365 double *y, *x, *xp, *yi, *psum, c0, c1, dx;
366 Array *array;
367 Dimension *tmp;
368 Operand opy, opx, opxp;
369 if (nArgs!=3 && nArgs!=4)
370 YError("integ takes exactly three or four arguments");
371
372 if (nArgs==4) {
373 which= YGetInteger(sp)-1; /* use 0-origin which here */
374 Drop(1);
375 } else {
376 which= 0; /* use 0-origin which here */
377 ClearTmpArray(); /* need a temporary for this calculation */
378 }
379
380 sp->ops->FormOperand(sp, &opxp);
381 (sp-1)->ops->FormOperand(sp-1, &opx);
382 (sp-2)->ops->FormOperand(sp-2, &opy);
383 opxp.ops->ToDouble(&opxp);
384 opx.ops->ToDouble(&opx);
385 opy.ops->ToDouble(&opy);
386
387 tmp= tmpDims;
388 tmpDims= 0;
389 FreeDimension(tmp);
390
391 /* compute dimensions of y array -- nfast-by-number-by-(slow), where
392 nfast are the first which dimensions of y, number is the length of
393 the dimension to interpolate on, and (slow) are the rest */
394 nDims= CountDims(opy.type.dims);
395 if (which<0) which= nDims+which; /* handle which<0 like array index<0 */
396 if (which>=nDims || which<0) YError("bad 4th argument to integ");
397 i= nDims-1-which;
398 tmp= opy.type.dims;
399 while (i) { tmp= tmp->next; i--; }
400 number= tmp? tmp->number : 1;
401 if (number<2) YError("bad dimension (length 1) in integ");
402 nfast= TotalNumber(tmp->next);
403 if (opx.type.number!=number || opx.type.dims->next)
404 YError("dimension of x does not match target dimension of y in integ");
405
406 if (which==nDims-1) {
407 /* can just tack new element(s) of dimension list onto old */
408 tmpDims= Ref(opy.type.dims->next);
409 tmpDims= CopyDims(opxp.type.dims, tmpDims, 1);
410 } else {
411 /* make a fresh copy of the index list and find where to insert new */
412 Dimension *prev, *tmpprev;
413 i= nDims-which; /* guaranteed >1 AND opy is at least 2D */
414 tmpDims= prev= CopyDims(opy.type.dims, tmpDims, 1);
415 tmp= prev->next; i--;
416 tmp= tmp->next; i--; /* prev is two behind tmp */
417 while (i) { prev= prev->next; tmp= tmp->next; i--; }
418 tmpprev= prev->next;
419 prev->next= 0; /* cut off tail of dimension list */
420 tmpprev->next= 0; /* this pointed to tmp */
421 FreeDimension(tmpprev);
422 prev->next= CopyDims(opxp.type.dims, tmp, 1);
423 }
424
425 /* create partial sum array (integrals up to each xp) */
426 array= NewTmpArray(&doubleStruct, opy.type.dims);
427 psum= array->value.d;
428
429 /* create result array */
430 array= PushDataBlock(NewArray(&doubleStruct, tmpDims));
431 yi= array->value.d;
432 y= opy.value;
433 x= opx.value;
434 xp= opxp.value;
435
436 /* The problem is as follows (same as interp):
437 For each value of the faster indices, and each value of the slower
438 indices of y, and for each value of xp, find the value of yi
439 corresponding to xp. Note that for each interpolation, the y
440 vector has stride nfast, while the x vector always has stride 0.
441 Note also that ALL of the yi for a given xp should be computed
442 once xp is found. */
443
444 /* first compute partial sums */
445 js= nfast*number;
446 for (j=0 ; j<opy.type.number ; j+=js) {
447 for (k=0 ; k<nfast ; k++) {
448 psum[k+j]= 0.0;
449 for (i=1, l=nfast ; i<number ; i++, l+=nfast)
450 psum[k+l+j]= psum[k+l-nfast+j]+
451 0.5*(y[k+l-nfast+j]+y[k+l+j])*(x[i]-x[i-1]);
452 }
453 }
454
455 /* use that to compute interpolated integrals */
456 ms= nfast*opxp.type.number;
457 c0= c1= 0.0;
458 ip= 0; /* hunt takes this as no guess on 1st pass */
459 for (i=l=0 ; i<opxp.type.number ; i++, l+=nfast) {
460 ip= hunt(x, number, xp[i], ip);
461 ipy= ip*nfast;
462 if (ip>=1 && ip<number) {
463 dx= xp[i]-x[ip-1];
464 c1= 0.5*dx*dx/(x[ip]-x[ip-1]);
465 c0= dx-c1;
466 }
467 for (j=m=0 ; j<opy.type.number ; j+=js, m+=ms) {
468 for (k=0 ; k<nfast ; k++) {
469 if (ip<1) { /* point below minimum */
470 yi[k+l+m]= 0.0;
471 } else if (ip<number) { /* point in range */
472 yi[k+l+m]= c0*y[k+(ipy-nfast)+j]+c1*y[k+ipy+j]
473 +psum[k+(ipy-nfast)+j];
474 } else { /* point above maximum */
475 yi[k+l+m]= psum[k+(ipy-nfast)+j];
476 }
477 }
478 }
479 }
480
481 ClearTmpArray();
482 PopToD(sp-4);
483 Drop(3);
484 }
485
486 /*--------------------------------------------------------------------------*/
487
488 /* The sorting algorithm implemented here is a hybrid of the algorithms
489 in section 4.10 of 2nd ed Kernighan and Ritchie and section 8.4 of
490 Numerical Recipes in C (Press et. al.).
491 The C library qsort routine does not allow for strides in an obvious
492 way, which is why Yorick needs these sorting routines. */
493
494 static long sortSize, sortStride, sortLimit;
495 static long *longData;
496 static double *doubleData;
497 static char **stringData;
498
499 static long Random(long range);
500 static void ysortQ(long *list, long n);
501 static void ysortD(long *list, long n);
502 static void ysortL(long *list, long n);
503
504 static long sortSeed= 0; /* for linear congruential random number
505 between 0 and 7874 inclusive,
506 (sortSeed*211+1663)%7875 next time */
507
Random(long range)508 static long Random(long range)
509 {
510 sortSeed= (sortSeed*211+1663) % 7875;
511 if (range <= 272730)
512 return (range*sortSeed)/7875;
513 else
514 return (((unsigned long)range)>>1) + (136365*sortSeed)/7875;
515 }
516
517 static int ystrcmp(const char *s, const char *t);
518 int
ystrcmp(const char * s,const char * t)519 ystrcmp(const char *s, const char *t)
520 {
521 if (!s) return t? -1 : 0;
522 if (!t) return s? 1 : 0;
523 return strcmp(s, t);
524 }
525
ysortQ(long * list,long n)526 static void ysortQ(long *list, long n)
527 {
528 long j;
529 if (n < sortLimit) {
530 /* straight insertion fastest for short sorts */
531 long i, listel;
532 char *partition;
533 for (i=sortStride ; i<n ; i+=sortStride) {
534 listel= list[i];
535 partition= stringData[listel];
536 for (j=i-sortStride ;
537 j>=0 && ystrcmp(stringData[list[j]], partition)>0 ;
538 j-=sortStride) list[j+sortStride]= list[j];
539 list[j+sortStride]= listel;
540 }
541 return; /* halt recursion */
542
543 } else {
544 /* generate random partition element, remember it (listel), and
545 replace it by the first element */
546 long i;
547 long partel= Random(n/sortStride)*sortStride;
548 long listel= list[partel];
549 char *partition= stringData[listel];
550 int scmp, flipper = 0;
551 list[partel]= list[0];
552 /* partition the remainder of the list into elements which precede
553 the partel then elements which follow it */
554 for (j=sortStride ; j<n ; j+=sortStride)
555 if (ystrcmp(stringData[list[j]], partition) >= 0) break;
556 for (i=j+sortStride ; i<n ; i+=sortStride) {
557 scmp = ystrcmp(stringData[list[i]], partition);
558 if (scmp > 0) continue;
559 if (scmp || ((flipper^=1))) {
560 register long tmp= list[j];
561 list[j]= list[i];
562 list[i]= tmp;
563 j+= sortStride; /* known to be >= partel (or == n) */
564 }
565 }
566 /* re-insert partition element at beginning of 2nd part of list
567 -- this will be its final resting place */
568 list[0]= list[j-sortStride];
569 list[j-sortStride]= listel;
570 }
571
572 /* recurse to sort the < and >= partitions themselves
573 This is outside previous if block to minimize stack space required
574 for the recursion. */
575 if (j>sortStride) ysortQ(list, j-sortStride);
576 if (j<n) ysortQ(&list[j], n-j);
577 }
578
ysortD(long * list,long n)579 static void ysortD(long *list, long n)
580 {
581 long j;
582 if (n < sortLimit) {
583 /* straight insertion fastest for short sorts */
584 long i, listel;
585 double partition;
586 for (i=sortStride ; i<n ; i+=sortStride) {
587 listel= list[i];
588 partition= doubleData[listel];
589 for (j=i-sortStride ;
590 j>=0 && doubleData[list[j]]>partition ;
591 j-=sortStride) list[j+sortStride]= list[j];
592 list[j+sortStride]= listel;
593 }
594 return; /* halt recursion */
595
596 } else {
597 /* generate random partition element, remember it (listel), and
598 replace it by the first element */
599 long i;
600 long partel= Random(n/sortStride)*sortStride;
601 long listel= list[partel];
602 double partition= doubleData[listel];
603 int flipper = 0;
604 list[partel]= list[0];
605 /* partition the remainder of the list into elements which precede
606 the partel then elements which follow it */
607 for (j=sortStride ; j<n ; j+=sortStride)
608 if (doubleData[list[j]] >= partition) break;
609 for (i=j+sortStride ; i<n ; i+=sortStride) {
610 if (doubleData[list[i]] > partition) continue;
611 if ((doubleData[list[i]] != partition) || ((flipper^=1))) {
612 register long tmp= list[j];
613 list[j]= list[i];
614 list[i]= tmp;
615 j+= sortStride; /* known to be >= partel (or == n) */
616 }
617 }
618 /* re-insert partition element at beginning of 2nd part of list
619 -- this will be its final resting place */
620 list[0]= list[j-sortStride];
621 list[j-sortStride]= listel;
622 }
623
624 /* recurse to sort the < and >= partitions themselves
625 This is outside previous if block to minimize stack space required
626 for the recursion. */
627 if (j>sortStride) ysortD(list, j-sortStride);
628 if (j<n) ysortD(&list[j], n-j);
629 }
630
ysortL(long * list,long n)631 static void ysortL(long *list, long n)
632 {
633 long j;
634 if (n < sortLimit) {
635 /* straight insertion fastest for short sorts */
636 long i, listel;
637 long partition;
638 for (i=sortStride ; i<n ; i+=sortStride) {
639 listel= list[i];
640 partition= longData[listel];
641 for (j=i-sortStride ;
642 j>=0 && longData[list[j]]>partition ;
643 j-=sortStride) list[j+sortStride]= list[j];
644 list[j+sortStride]= listel;
645 }
646 return; /* halt recursion */
647
648 } else {
649 /* generate random partition element, remember it (listel), and
650 replace it by the first element */
651 long i;
652 long partel= Random(n/sortStride)*sortStride;
653 long listel= list[partel];
654 long partition= longData[listel];
655 int flipper = 0;
656 list[partel]= list[0];
657 /* partition the remainder of the list into elements which precede
658 the partel then elements which follow it */
659 for (j=sortStride ; j<n ; j+=sortStride)
660 if (longData[list[j]] >= partition) break;
661 for (i=j+sortStride ; i<n ; i+=sortStride) {
662 if (longData[list[i]] > partition) continue;
663 if ((longData[list[i]] != partition) || ((flipper^=1))) {
664 register long tmp= list[j];
665 list[j]= list[i];
666 list[i]= tmp;
667 j+= sortStride; /* known to be >= partel (or == n) */
668 }
669 }
670 /* re-insert partition element at beginning of 2nd part of list
671 -- this will be its final resting place */
672 list[0]= list[j-sortStride];
673 list[j-sortStride]= listel;
674 }
675
676 /* recurse to sort the < and >= partitions themselves
677 This is outside previous if block to minimize stack space required
678 for the recursion. */
679 if (j>sortStride) ysortL(list, j-sortStride);
680 if (j<n) ysortL(&list[j], n-j);
681 }
682
Y_sort(int nArgs)683 void Y_sort(int nArgs)
684 {
685 Operand op;
686 Array *result;
687 long *ilist, i, j, which, nDims, origin;
688 Dimension *tmp;
689 void (*ysort)(long *list, long n);
690 if (nArgs!=1 && nArgs!=2)
691 YError("sort takes exactly one or two arguments");
692
693 if (nArgs==2) { which= YGetInteger(sp)-1; Drop(1); }
694 else which= 0; /* use 0-origin which here */
695
696 /* get array to be sorted */
697 sp->ops->FormOperand(sp, &op);
698 if (op.ops->typeID <= T_LONG) {
699 op.ops->ToLong(&op);
700 ysort= &ysortL;
701 longData= op.value;
702 } else if (op.ops->typeID <= T_DOUBLE) {
703 op.ops->ToDouble(&op);
704 ysort= &ysortD;
705 doubleData= op.value;
706 } else if (op.ops==&stringOps) {
707 ysort= &ysortQ;
708 stringData= op.value;
709 } else {
710 YError("sort function requires integer, real, or string operand");
711 ysort= 0;
712 }
713
714 /* figure out stride for the sort */
715 nDims= CountDims(op.type.dims);
716 if (nDims==0) {
717 PushIntValue(0);
718 sp->ops= &longScalar;
719 sp->value.l= 0;
720 return;
721 }
722 if (which<0) which+= nDims;
723 if (which<0 || which>=nDims)
724 YError("2nd argument to sort function out of range");
725 if (nDims<2) {
726 sortStride= 1;
727 sortSize= op.type.number;
728 } else {
729 which= nDims-1-which;
730 tmp= op.type.dims;
731 while (which--) tmp= tmp->next;
732 sortStride= TotalNumber(tmp->next);
733 sortSize= sortStride*tmp->number;
734 }
735 sortLimit= 7*sortStride; /* use straight insertion for <7 elements */
736
737 /* push result Array, then fill it with index to be sorted */
738 result= PushDataBlock(NewArray(&longStruct, op.type.dims));
739 ilist= result->value.l;
740 for (i=0 ; i<op.type.number ; i++) ilist[i]= i;
741
742 for (i=0 ; i<sortStride ; i++)
743 for (j=0 ; j<op.type.number ; j+=sortSize)
744 ysort(&ilist[i+j], sortSize);
745
746 if ((origin= op.type.dims->origin))
747 for (i=0 ; i<op.type.number ; i++) ilist[i]+= origin;
748 }
749
750 /*--------------------------------------------------------------------------*/
751
Y_transpose(int nArgs)752 void Y_transpose(int nArgs)
753 {
754 Symbol *stack= sp-nArgs+1;
755 Operand op, opp;
756 int i, nDims, order[10];
757 long numbers[10], origins[10], strides[10];
758 long stride, *cycle, index, prev, next, last;
759 Dimension *dims;
760 LValue *lvalue;
761 Strider *strider;
762 if (nArgs < 1) YError("transpose needs at least argument");
763
764 stack->ops->FormOperand(stack, &op);
765 if (!op.ops->isArray) YError("1st argument to transpose must be array");
766 nDims= CountDims(op.type.dims);
767 if (nDims>10) YError("transpose fails for arrays with >10 dimensions");
768 if (nDims<1) {
769 if (nArgs>1) Drop(nArgs-1);
770 return;
771 }
772
773 /* collect dimension lengths and strides into arrays to be permuted */
774 dims= op.type.dims;
775 stride= 1;
776 for (i=0 ; i<nDims ; i++) {
777 numbers[nDims-1-i]= dims->number;
778 origins[nDims-1-i]= dims->origin;
779 dims= dims->next;
780 }
781 stride= op.type.base->size;
782 for (i=0 ; i<nDims ; i++) {
783 strides[i]= stride;
784 stride*= numbers[i];
785 }
786
787 /* compute the permutation from the remaining arguments */
788 for (i=0 ; i<nDims ; i++) order[i]= i;
789 if (nArgs<2) {
790 /* default is to swap first and last indices */
791 if (nDims) {
792 prev= order[0];
793 order[0]= order[nDims-1];
794 order[nDims-1]= prev;
795 }
796 } else {
797 /* read permutation list */
798 while (stack<sp) {
799 stack++;
800 stack->ops->FormOperand(stack, &opp);
801 if (opp.ops->promoteID>T_LONG ||
802 (opp.type.dims && opp.type.dims->next))
803 YError("bad permutation list in transpose");
804 opp.ops->ToLong(&opp);
805 cycle= opp.value;
806 if (opp.type.dims) {
807 /* this is a cycle list */
808 last= cycle[0]-1;
809 if (last<0) last+= nDims;
810 if (last<0 || last>=nDims)
811 YError("permutation list references non-existent dimension "
812 "in transpose");
813 prev= order[last];
814 for (i=1 ; i<opp.type.number ; i++) {
815 index= cycle[i]-1;
816 if (index<0) index+= nDims;
817 if (index<0 || index>=nDims)
818 YError("permutation list references non-existent dimension "
819 "in transpose");
820 next= order[index];
821 order[index]= prev;
822 prev= next;
823 }
824 order[last]= prev;
825 } else {
826 /* this is a cyclic permutation of all nDims indices */
827 long inc= cycle[0]-1; /* index which 0 should go to */
828 long now;
829 if (inc<0) inc= nDims - (-inc)%nDims;
830 if (inc>=nDims) inc%= nDims;
831 prev= order[0];
832 now= inc;
833 last= now;
834 for (i=0 ; i<nDims ; i++) {
835 next= order[now];
836 order[now]= prev;
837 prev= next;
838 now+= inc;
839 if (now>=nDims) now-= nDims;
840 if (last==now) {
841 /* handle case of several independent cycles when nDims is
842 evenly divisible by inc */
843 prev= order[++now];
844 now+= inc;
845 if (now>=nDims) now-= nDims;
846 last= now;
847 }
848 }
849 }
850 }
851 Drop(nArgs-1);
852 }
853
854 /* build re-ordered dimension list */
855 dims= tmpDims;
856 tmpDims= 0;
857 FreeDimension(dims);
858 for (i=0 ; i<nDims ; i++)
859 tmpDims= NewDimension(numbers[order[i]], origins[order[i]], tmpDims);
860
861 /* push LValue describing result onto stack */
862 lvalue= PushDataBlock(NewLValueM((Array *)sp->value.db, op.value,
863 op.type.base, tmpDims));
864
865 /* build strider list describing re-ordering */
866 for (i=0 ; i<nDims ; i++) {
867 strider= NewStrider(strides[order[i]], numbers[order[i]]);
868 strider->next= lvalue->strider;
869 lvalue->strider= strider;
870 }
871
872 PopTo(sp-2);
873 Drop(1);
874 FetchLValue(lvalue, sp);
875 }
876
877 static Dimension *growDims= 0;
878
Y_grow(int nArgs)879 void Y_grow(int nArgs)
880 {
881 Symbol *s0, *s= sp-nArgs+1;
882 long index= s->index;
883 Array *array;
884 Dimension *dims;
885 StructDef *base;
886 Operand op;
887 long extra, number;
888 int nDims;
889 DataBlock *db;
890 int amSubroutine= CalledAsSubroutine();
891
892 if (nArgs < 2) YError("grow function needs at least two arguments");
893 if (amSubroutine && s->ops!=&referenceSym)
894 YError("1st argument to grow must be a variable reference");
895 if (!s->ops) YError("unxepected keyword argument in grow");
896
897 dims= growDims;
898 growDims= 0;
899 FreeDimension(dims);
900
901 /* scan argument list to find first non-nil argument */
902 base= 0;
903 s0= 0;
904 for (;;) {
905 if (!s0 && amSubroutine) array= (Array *)ForceToDB(&globTab[index]);
906 else array= (Array *)ForceToDB(s); /* does ReplaceRef if required */
907 s0= s;
908 if (array->ops==&lvalueOps) array= FetchLValue(array, s);
909 if (array->ops->isArray) {
910 base= array->type.base;
911 if (array->references) {
912 /* the grow operation is destructive, must copy 1st arg */
913 Array *copy= PushDataBlock(NewArray(base, array->type.dims));
914 base->Copy(base, copy->value.c, array->value.c, array->type.number);
915 PopTo(s);
916 array= copy;
917 }
918 if (array->type.dims) {
919 growDims= NewDimension(1L, 1L, Ref(array->type.dims->next));
920 } else {
921 growDims= NewDimension(1L, 1L, (Dimension *)0);
922 array->type.dims= NewDimension(1L, 1L, (Dimension *)0);
923 }
924 break;
925 } else if (array->ops!=&voidOps) {
926 YError("bad data type in function grow");
927 }
928 if (++s > sp) { /* all arguments void, will return nil */
929 Drop(nArgs-1);
930 PopTo(sp-1);
931 return;
932 }
933 }
934 nDims= CountDims(growDims);
935
936 /* scan through remaining arguments to force right-conformability with
937 growDims and count the number of extra dimensions */
938 extra= 0;
939 while (s<sp) {
940 s++;
941 if (!s->ops) YError("unxepected keyword argument in grow");
942 s->ops->FormOperand(s, &op);
943 if (op.ops->isArray) {
944 if (nDims==CountDims(op.type.dims))
945 growDims->number= op.type.dims->number;
946 else
947 growDims->number= 1;
948 if (RightConform(growDims, &op))
949 YError("later arguments not conformable with 1st in grow");
950 extra+= growDims->number;
951 } else if (op.ops!=&voidOps) {
952 YError("illegal data type in function grow");
953 }
954 }
955
956 if (extra) {
957 LValue lvalue;
958 long size;
959 BinaryOp *Assign= base->dataOps->Assign;
960
961 /* phony LValue necessary for Assign virtual function */
962 lvalue.references= nArgs; /* NEVER want to free this */
963 lvalue.ops= &lvalueOps;
964 lvalue.owner= 0; /* not true, but safer */
965 lvalue.type.base= base; /* NOT Ref(base) -- won't be freed */
966 lvalue.address.m= 0;
967 lvalue.strider= 0;
968
969 size= base->size;
970 /* copy 1st non-nil argument */
971 number= array->type.number;
972 array= PushDataBlock(GrowArray(array, extra));
973 lvalue.address.m= array->value.c + size*number;
974
975 /* second pass through argument list copies 2nd-Nth arguments
976 into result array using the Assign virtual function */
977 s= s0;
978 while (++s<sp) { /* note that sp is bigger than for previous loop */
979 s->ops->FormOperand(s, &op);
980 if (op.ops->isArray) {
981 lvalue.type.dims= op.type.dims; /* NOT Ref(dims) -- won't be freed */
982 lvalue.type.number= op.type.number;
983 /* Assign virtual functions assume their first parameter is an
984 LValue* rather than an Operation* (like all other BinaryOps). */
985 (*Assign)((Operand *)&lvalue, &op);
986 lvalue.address.m+= size*lvalue.type.number;
987 }
988 }
989 }
990
991 /* store result back to first reference -- will also be left on stack
992 by EvalBI */
993 if (amSubroutine) {
994 s= &globTab[index]; /* guaranteed this is a dataBlockSym by ForceToDB */
995 db= s->value.db;
996 s->value.db= (DataBlock *)Ref(array);
997 Unref(db);
998 if (extra) Drop(nArgs);
999 else Drop(nArgs-1);
1000 ReplaceRef(sp); /* result is 1st argument */
1001 PopTo(sp-1);
1002 } else {
1003 if (extra) { /* result is on top of stack */
1004 PopTo(sp-nArgs-1);
1005 Drop(nArgs);
1006 } else { /* result is unchanged s0 argument */
1007 int nAbove= sp-s0;
1008 Drop(nAbove);
1009 nArgs-= nAbove;
1010 PopTo(sp-nArgs);
1011 Drop(nArgs-1);
1012 }
1013 }
1014 }
1015
Y__(int nArgs)1016 void Y__(int nArgs)
1017 {
1018 Y_grow(nArgs);
1019 }
1020
1021 /*--------------------------------------------------------------------------*/
1022
1023 extern char *Ytimestamp(void); /* 25 character return */
1024
1025 void
Y_timestamp(int argc)1026 Y_timestamp(int argc)
1027 {
1028 time_t n_time = time((void *)0);
1029 char *time = ctime(&n_time);
1030 long index = yget_ref(0);
1031 if (argc != 1) y_error("timestamp takes exactly one argument");
1032 if (index >= 0) {
1033 ypush_long((long)n_time);
1034 yput_global(index, 0);
1035 }
1036 if (!yarg_subroutine()) {
1037 char **q = ypush_q(0);
1038 q[0] = p_strcpy(strtok(time, "\n"));
1039 }
1040 }
1041
1042 static double y_wall0 = 0.;
1043
Y_timer(int nArgs)1044 void Y_timer(int nArgs)
1045 {
1046 Operand op;
1047 double *absTime, *incTime, cpu, sys, wall;
1048
1049 if (nArgs < 0) {
1050 y_wall0= p_wall_secs();
1051 return;
1052 }
1053
1054 if (nArgs<1 || nArgs>2 || !sp->ops)
1055 YError("timer takes exactly one or two arguments");
1056 sp->ops->FormOperand(sp, &op);
1057 if (op.ops!=&doubleOps || op.type.number!=3)
1058 YError("timer arguments must be array(double,3)");
1059 if (nArgs==1) {
1060 absTime= op.value;
1061 incTime= 0;
1062 } else {
1063 incTime= op.value;
1064 (sp-1)->ops->FormOperand(sp-1, &op);
1065 if (op.ops!=&doubleOps || op.type.number!=3)
1066 YError("timer arguments must be array(double,3)");
1067 absTime= op.value;
1068 }
1069
1070 cpu= p_cpu_secs(&sys);
1071 wall= p_wall_secs() - y_wall0;
1072 if (incTime) {
1073 incTime[0]+= cpu-absTime[0];
1074 incTime[1]+= sys-absTime[1];
1075 incTime[2]+= wall-absTime[2];
1076 }
1077 absTime[0]= cpu;
1078 absTime[1]= sys;
1079 absTime[2]= wall;
1080
1081 Drop(nArgs);
1082 }
1083
1084 /*--------------------------------------------------------------------------*/
1085
1086 extern void BuildDimList(Symbol *stack, int nArgs); /* ops3.c */
1087 static void NextRandom(double *random, long n);
1088 static void InitRandom(double seed);
1089
Y_random(int nArgs)1090 void Y_random(int nArgs)
1091 {
1092 Symbol *stack= sp-nArgs+1;
1093 double *random;
1094 long n;
1095 if (nArgs==1 && !YNotNil(stack)) {
1096 /* return scalar result */
1097 PushDoubleValue(0.0);
1098 random= &sp->value.d;
1099 n= 1;
1100 } else {
1101 /* return array result */
1102 Array *array;
1103 BuildDimList(stack, nArgs);
1104 array= PushDataBlock(NewArray(&doubleStruct, tmpDims));
1105 random= array->value.d;
1106 n= array->type.number;
1107 }
1108 NextRandom(random, n);
1109 }
1110
Y_random_seed(int nArgs)1111 void Y_random_seed(int nArgs)
1112 {
1113 double seed= 0.0;
1114 if (nArgs==1) {
1115 if (YNotNil(sp)) seed= YGetReal(sp);
1116 } else if (nArgs>0) {
1117 YError("random_seed takes exactly zero or one arguments");
1118 }
1119 InitRandom(seed);
1120 }
1121
1122 /* Algorithm from Press and Teukolsky, Computers in Physics, 6, #5,
1123 Sep/Oct 1992, pp. 522-524. They offer a $1000 reward to anyone
1124 who finds a statistical test this generator fails non-trivially.
1125 Based on a generator of L'Ecuyer with a shuffle algorithm of
1126 Bays-Durham and other improvements.
1127 The period of the generator is 2.3e18. */
1128
1129 /* The long period is achieved by combining two sequences with
1130 nearly incomensurate periods. */
1131 static long idum= 0; /* this is the primary seed */
1132 static long idum2= 0; /* this is the secondary seed */
1133 #undef NTAB
1134 #define NTAB 32
1135 static long iv[NTAB], iy; /* shuffle table (of idum) and previous result */
1136
1137 /* Choose two sets of linear congruential parameters-- the
1138 multiplier is IA, and the modulus is IM. The IR and IQ are
1139 cunningly chosen so that (IA*x)%IM can be computed as
1140 IA*(x%IQ) - IR*(x/IQ), the latter expression having the
1141 advantage that the product will not overflow. The IM values
1142 are near 2^31-1, the largest guaranteed positive long.
1143 The clever calculation of (IA*x)%IM is due to Schrage,
1144 ACM Trans. Mathem. Software 5, 132-138;
1145 IM= IA*IQ+IR with IR<IQ is the required relation. */
1146 #undef IM1
1147 #define IM1 2147483563
1148 #undef IA1
1149 #define IA1 40014
1150 #undef IQ1
1151 #define IQ1 53668
1152 #undef IR1
1153 #define IR1 12211
1154
1155 #undef IM2
1156 #define IM2 2147483399
1157 #undef IA2
1158 #define IA2 40692
1159 #undef IQ2
1160 #define IQ2 52774
1161 #undef IR2
1162 #define IR2 3791
1163
1164 #undef AM
1165 #define AM (1.0/(double)IM1)
1166 #undef IMM1
1167 #define IMM1 (IM1-1)
1168 #undef NDIV
1169 #define NDIV (1+IMM1/NTAB)
1170
NextRandom(double * random,long n)1171 static void NextRandom(double *random, long n)
1172 {
1173 long j;
1174 if (idum<=0) InitRandom(0.0);
1175
1176 while (n--) {
1177 /* compute idum= (IA1*idum)%IM1 without overflow */
1178 idum= IA1*(idum%IQ1) - IR1*(idum/IQ1);
1179 if (idum<0) idum+= IM1;
1180
1181 /* compute idum2= (IA2*idum2)%IM2 without overflow */
1182 idum2= IA2*(idum2%IQ2) - IR2*(idum2/IQ2);
1183 if (idum2<0) idum2+= IM2;
1184
1185 /* previous result is used to determine which element of the shuffle
1186 table to use for this result */
1187 j= iy/NDIV; /* in range 0..NTAB-1 */
1188 iy= iv[j]-idum2;
1189 iv[j]= idum;
1190 if (iy<1) iy+= IMM1;
1191
1192 /* Really only IMM1 possible values can be returned, 1<=iy<=IMM1.
1193 Algorithm given by Press and Teukolsky has a slight bug.
1194 Here, the return values are centered in IMM1 equal bins.
1195 If 2.e9 distinct values are not enough, could use, say, idum2
1196 to scoot the points around randomly within bins... */
1197 *random++= AM*(iy-0.5);
1198 }
1199 }
1200
InitRandom(double seed)1201 static void InitRandom(double seed)
1202 {
1203 long j;
1204
1205 /* translate seed to integer between 1 and IM2-1 inclusive */
1206 if (seed<=0.0 || seed>=1.0) seed= 0.6180339885; /* default seed */
1207 idum= (long)(seed*(double)IM2);
1208 if (idum<1 || idum>=IM2) idum= 1;
1209 idum2= idum;
1210
1211 /* do 8 warm-ups, then load shuffle table */
1212 for (j=NTAB+7 ; j>=0 ; j--) {
1213 idum= IA1*(idum%IQ1) - IR1*(idum/IQ1);
1214 if (idum<0) idum+= IM1;
1215 if (j<NTAB) iv[j]= idum;
1216 }
1217 iy= iv[0];
1218 }
1219
1220 /*--------------------------------------------------------------------------*/
1221
1222 static void MrgCpyC(StructDef *, void *, void *, void *, int *, long);
1223 static void MrgCpyS(StructDef *, void *, void *, void *, int *, long);
1224 static void MrgCpyI(StructDef *, void *, void *, void *, int *, long);
1225 static void MrgCpyL(StructDef *, void *, void *, void *, int *, long);
1226 static void MrgCpyF(StructDef *, void *, void *, void *, int *, long);
1227 static void MrgCpyD(StructDef *, void *, void *, void *, int *, long);
1228 static void MrgCpyZ(StructDef *, void *, void *, void *, int *, long);
1229 static void MrgCpyX(StructDef *, void *, void *, void *, int *, long);
1230
1231 static void (*MrgCpy[10])(StructDef*, void*, void*, void*, int*, long)= {
1232 &MrgCpyC, &MrgCpyS, &MrgCpyI, &MrgCpyL, &MrgCpyF, &MrgCpyD, &MrgCpyZ,
1233 &MrgCpyX, &MrgCpyX, &MrgCpyX };
1234
1235 extern VMaction True;
1236
Y_merge(int nArgs)1237 void Y_merge(int nArgs)
1238 {
1239 Operand t, f;
1240 Operations *ops;
1241 Dimension *dims;
1242 int *cond;
1243 long i, n;
1244 void *rslt= 0;
1245 StructDef *base;
1246
1247 if (nArgs!=3) YError("merge function takes exactly three arguments");
1248 if (sp->ops==&referenceSym) ReplaceRef(sp);
1249 True(); /* convert condition to type int, values 0 or 1 */
1250 sp->ops->FormOperand(sp, &t);
1251 dims= t.type.dims;
1252 n= t.type.number;
1253 cond= t.value;
1254 if (!(sp-2)->ops)
1255 YError("merge function recognizes no keyword arguments");
1256 (sp-2)->ops->FormOperand(sp-2, &t);
1257 (sp-1)->ops->FormOperand(sp-1, &f);
1258 if (t.ops==&voidOps) {
1259 t.type.number= 0;
1260 if (f.ops==&voidOps) f.type.number= 0;
1261 ops= f.ops;
1262 base= f.type.base;
1263 } else if (f.ops==&voidOps) {
1264 f.type.number= 0;
1265 ops= t.ops;
1266 base= t.type.base;
1267 } else {
1268 ops= t.ops->Promote[f.ops->promoteID](&t, &f);
1269 base= t.type.base;
1270 if (ops==&structOps && !StructEqual(base, f.type.base))
1271 YError("two different struct instance types cannot be merged");
1272 }
1273 if (!ops || !ops->isArray)
1274 YError("merge requires array or nil arguments");
1275 if (t.type.number+f.type.number != n)
1276 YError("number of trues + number of falses not number of conditions");
1277
1278 if (!dims) {
1279 if (base==&doubleStruct) {
1280 PushDoubleValue(0.0);
1281 rslt= &sp->value.d;
1282 } else if (base==&longStruct) {
1283 PushLongValue(0L);
1284 rslt= &sp->value.l;
1285 } else if (base==&intStruct) {
1286 PushIntValue(0);
1287 rslt= &sp->value.i;
1288 }
1289 }
1290 if (!rslt) {
1291 Array *result= PushDataBlock(NewArray(base, dims));
1292 rslt= result->value.c;
1293 }
1294
1295 for (i=0 ; i<n ; i++) if (cond[i]) t.type.number--;
1296 if (t.type.number)
1297 YError("number of falses does not match number of 0 conditions");
1298 MrgCpy[ops->typeID](base, rslt, t.value, f.value, cond, n);
1299 }
1300
1301 #undef OPERATION
1302 #define OPERATION(op, typd) \
1303 static void op(StructDef *base, void*rr, void*tt, void*ff, int*c, long n) \
1304 { typd *r= rr, *t= tt, *f= ff; long i; \
1305 for (i=0 ; i<n ; i++) r[i]= c[i]? *t++ : *f++; }
1306
1307 /* ARGSUSED */
OPERATION(MrgCpyC,char)1308 OPERATION(MrgCpyC, char)
1309 /* ARGSUSED */
1310 OPERATION(MrgCpyS, short)
1311 /* ARGSUSED */
1312 OPERATION(MrgCpyI, int)
1313 /* ARGSUSED */
1314 OPERATION(MrgCpyL, long)
1315 /* ARGSUSED */
1316 OPERATION(MrgCpyF, float)
1317 /* ARGSUSED */
1318 OPERATION(MrgCpyD, double)
1319
1320 /* ARGSUSED */
1321 static void MrgCpyZ(StructDef *base, void*rr, void*tt, void*ff, int*c, long n)
1322 {
1323 double *r= rr, *t= tt, *f= ff;
1324 long i;
1325 n*= 2;
1326 for (i=0 ; i<n ; i+=2) {
1327 if (c[i>>1]) { r[i]= *t++; r[i+1]= *t++; }
1328 else { r[i]= *f++; r[i+1]= *f++; }
1329 }
1330 }
1331
MrgCpyX(StructDef * base,void * rr,void * tt,void * ff,int * c,long n)1332 static void MrgCpyX(StructDef *base, void*rr, void*tt, void*ff, int*c, long n)
1333 {
1334 char *r= rr, *t= tt, *f= ff;
1335 long size= base->size;
1336 Copier *Copy= base->Copy;
1337 int cc= c[0];
1338 long nn, i= 0;
1339 do {
1340 i++;
1341 if (cc) {
1342 for (nn=1 ; i<n && c[i] ; i++) nn++;
1343 Copy(base, r, t, nn);
1344 nn*= size;
1345 r+= nn;
1346 t+= nn;
1347 cc= 0;
1348 } else {
1349 for (nn=1 ; i<n && !c[i] ; i++) nn++;
1350 Copy(base, r, f, nn);
1351 nn*= size;
1352 r+= nn;
1353 f+= nn;
1354 cc= 1;
1355 }
1356 } while (i<n);
1357 }
1358
1359 /*--------------------------------------------------------------------------*/
1360
1361 #undef N_KEYWORDS
1362 #define N_KEYWORDS 1
1363 static char *histKeys[N_KEYWORDS+1]= { "top", 0 };
1364
Y_histogram(int nArgs)1365 void Y_histogram(int nArgs)
1366 {
1367 Symbol *keySymbols[N_KEYWORDS];
1368 Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, histKeys, keySymbols);
1369 int iPass= 0;
1370 long *list= 0;
1371 double *weight= 0;
1372 Dimension *dimsl, *dimsw;
1373 long number, i, top;
1374
1375 while (stack<=sp) {
1376 if (!stack->ops) { stack+= 2; continue; }
1377 if (iPass==0) list= YGet_L(stack, 0, &dimsl);
1378 else if (iPass==1) weight= YGet_D(stack, 0, &dimsw);
1379 else list= 0; /* error */
1380 iPass++;
1381 stack++;
1382 }
1383 if (!list) YError("histogram takes one or two non-keyword arguments");
1384 number= TotalNumber(dimsl);
1385 if (weight && TotalNumber(dimsw)!=number)
1386 YError("histogram weight array must be same length as list");
1387
1388 top= 1;
1389 for (i=0 ; i<number ; i++) {
1390 if (list[i]>top) top= list[i];
1391 else if (list[i]<1) YError("histogram list element < 1 illegal");
1392 }
1393
1394 if (YNotNil(keySymbols[0])) {
1395 i= YGetInteger(keySymbols[0]);
1396 if (i<top) YError("histogram list element > top illegal");
1397 top= i;
1398 }
1399
1400 dimsl= tmpDims;
1401 tmpDims= 0;
1402 FreeDimension(dimsl);
1403 tmpDims= NewDimension(top, 1L, (Dimension *)0);
1404
1405 if (!weight) {
1406 Array *array= PushDataBlock(NewArray(&longStruct, tmpDims));
1407 long *hist= array->value.l;
1408 for (i=0 ; i<top ; i++) hist[i]= 0;
1409 for (i=0 ; i<number ; i++) hist[list[i]-1]++;
1410 } else {
1411 Array *array= PushDataBlock(NewArray(&doubleStruct, tmpDims));
1412 double *hist= array->value.d;
1413 for (i=0 ; i<top ; i++) hist[i]= 0.0;
1414 for (i=0 ; i<number ; i++) hist[list[i]-1]+= weight[i];
1415 }
1416 }
1417
1418 /*--------------------------------------------------------------------------*/
1419
Y_poly(int nArgs)1420 void Y_poly(int nArgs)
1421 {
1422 Symbol *stack, *xsp= sp - nArgs + 1;
1423 extern void Multiply(void);
1424 extern void Add(void);
1425
1426 for (stack=xsp ; stack<=sp ; stack++) {
1427 if (!stack->ops) YError("poly accepts no keyword arguments");
1428 if (stack->ops==&referenceSym) ReplaceRef(stack);
1429 if (stack->ops==&dataBlockSym && stack->value.db->ops==&lvalueOps)
1430 FetchLValue(stack->value.db, stack);
1431 }
1432
1433 while (sp>xsp+1) {
1434 PushCopy(xsp);
1435 Multiply();
1436 Add();
1437 }
1438 }
1439
1440 /*--------------------------------------------------------------------------*/
1441
1442 void
Y_noop(int argc)1443 Y_noop(int argc)
1444 {
1445 if (argc && sp->ops==&referenceSym) ReplaceRef(sp);
1446 return;
1447 }
1448
1449 /*--------------------------------------------------------------------------*/
1450