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