]> git.vpit.fr Git - perl/modules/Sub-Nary.git/blob - Nary.xs
7a78e938aaf92804a91840b40ca81b438617424c
[perl/modules/Sub-Nary.git] / Nary.xs
1 /* This file is part of the Sub::Nary Perl module.
2  * See http://search.cpan.org/dist/Sub::Nary/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #ifndef mPUSHi
10 # define mPUSHi(I) PUSHs(sv_2mortal(newSViv(I)))
11 #endif /* !mPUSHi */
12
13 typedef struct {
14  UV k;
15  NV v;
16 } sn_combcache;
17
18 STATIC void sn_store(pTHX_ HV *tb, const char *key, I32 klen, SV *val, U32 hash) {
19 #define sn_store(T, K, KL, V, H) sn_store(aTHX_ (T), (K), (KL), (V), (H))
20  if (!hv_store(tb, key, klen, val, hash))
21   SvREFCNT_dec(val);
22 }
23
24 STATIC void sn_store_ent(pTHX_ HV *tb, SV *key, SV *val, U32 hash) {
25 #define sn_store_ent(T, K, V, H) sn_store_ent(aTHX_ (T), (K), (V), (H))
26  if (!hv_store_ent(tb, key, val, hash))
27   SvREFCNT_dec(val);
28 }
29
30
31 STATIC U32 sn_hash_list = 0;
32
33 /* --- XS ------------------------------------------------------------------ */
34
35 MODULE = Sub::Nary            PACKAGE = Sub::Nary
36
37 PROTOTYPES: ENABLE
38
39 BOOT:
40 {
41  PERL_HASH(sn_hash_list, "list", 4);
42 }
43
44 void
45 tag(SV *op)
46 PROTOTYPE: $
47 CODE:
48  ST(0) = sv_2mortal(newSVuv(SvUV(SvRV(op))));
49  XSRETURN(1);
50
51 void
52 null(SV *op)
53 PROTOTYPE: $
54 PREINIT:
55  OP *o;
56 CODE:
57  o = INT2PTR(OP *, SvUV(SvRV(op)));
58  ST(0) = sv_2mortal(newSVuv(o == NULL));
59  XSRETURN(1);
60
61 void
62 zero(SV *sv)
63 PROTOTYPE: $
64 PREINIT:
65  HV *hv;
66  IV res;
67 CODE:
68  if (!SvOK(sv))
69   XSRETURN_IV(1);
70  if (!SvROK(sv)) {
71   res = SvNOK(sv) ? SvNV(sv) == 0.0 : SvUV(sv) == 0;
72   XSRETURN_IV(res);
73  }
74  hv = (HV *) SvRV(sv);
75  res = hv_iterinit(hv) == 1 && hv_exists(hv, "0", 1);
76  XSRETURN_IV(res);
77
78 void
79 count(SV *sv)
80 PROTOTYPE: $
81 PREINIT:
82  HV *hv;
83  HE *key;
84  NV c = 0;
85 CODE:
86  if (!SvOK(sv))
87   XSRETURN_IV(0);
88  if (!SvROK(sv))
89   XSRETURN_IV(1);
90  hv = (HV *) SvRV(sv);
91  hv_iterinit(hv);
92  while (key = hv_iternext(hv)) {
93   c += SvNV(HeVAL(key));
94  }
95  XSRETURN_NV(c);
96
97 void
98 normalize(SV *sv)
99 PROTOTYPE: $
100 PREINIT:
101  HV *hv, *res;
102  HE *key;
103  NV c = 0;
104 CODE:
105  if (!SvOK(sv))
106   XSRETURN_UNDEF;
107  res = newHV();
108  if (!SvROK(sv)) {
109   sn_store_ent(res, sv, newSVuv(1), 0);
110  } else {
111   hv = (HV *) SvRV(sv);
112   if (!hv_iterinit(hv)) {
113    sn_store(res, "0", 1, newSVuv(1), 0);
114   } else {
115    while (key = hv_iternext(hv))
116     c += SvNV(HeVAL(key));
117    hv_iterinit(hv);
118    while (key = hv_iternext(hv)) {
119     SV *val = newSVnv(SvNV(HeVAL(key)) / c);
120     sn_store_ent(res, HeSVKEY_force(key), val, HeHASH(key));
121    }
122   }
123  }
124  ST(0) = sv_2mortal(newRV_noinc((SV *) res));
125  XSRETURN(1);
126
127 void
128 scale(SV *csv, SV *sv)
129 PROTOTYPE: $;$
130 PREINIT:
131  HV *hv, *res;
132  HE *key;
133  NV c = 1;
134 CODE:
135  if (!SvOK(sv))
136   XSRETURN_UNDEF;
137  if (SvOK(csv))
138   c = SvNV(csv);
139  res = newHV();
140  if (!SvROK(sv)) {
141   sn_store_ent(res, sv, newSVnv(c), 0);
142  } else {
143   hv = (HV *) SvRV(sv);
144   if (!hv_iterinit(hv)) {
145    sn_store(res, "0", 1, newSVnv(c), 0);
146   } else {
147    hv_iterinit(hv);
148    while (key = hv_iternext(hv)) {
149     SV *val = newSVnv(SvNV(HeVAL(key)) * c);
150     sn_store_ent(res, HeSVKEY_force(key), val, HeHASH(key));
151    }
152   }
153  }
154  ST(0) = sv_2mortal(newRV_noinc((SV *) res));
155  XSRETURN(1);
156
157 void
158 add(...)
159 PROTOTYPE: @
160 PREINIT:
161  HV *res;
162  SV *cur, *val;
163  HE *key, *old;
164  I32 i;
165 CODE:
166  if (!items)
167   XSRETURN_UNDEF;
168  res = newHV();
169  for (i = 0; i < items; ++i) {
170   cur = ST(i);
171   if (!SvOK(cur))
172    continue;
173   if (!SvROK(cur)) {
174    NV v = 1;
175    if ((old = hv_fetch_ent(res, cur, 1, 0)) && SvOK(val = HeVAL(old)))
176     v += SvNV(val);
177    sn_store_ent(res, cur, newSVnv(v), 0);
178    continue;
179   }
180   cur = SvRV(cur);
181   hv_iterinit((HV *) cur);
182   while (key = hv_iternext((HV *) cur)) {
183    SV *k = HeSVKEY_force(key);
184    NV  v = SvNV(HeVAL(key));
185    if ((old = hv_fetch_ent(res, k, 1, 0)) && SvOK(val = HeVAL(old)))
186     v += SvNV(val);
187    sn_store_ent(res, k, newSVnv(v), 0);
188   }
189  }
190  if (!hv_iterinit(res)) {
191   SvREFCNT_dec(res);
192   XSRETURN_UNDEF;
193  }
194  ST(0) = sv_2mortal(newRV_noinc((SV *) res));
195  XSRETURN(1);
196
197 void
198 cumulate(SV *sv, SV *nsv, SV *csv)
199 PROTOTYPE: $$$
200 PREINIT:
201  HV *res;
202  HE *key;
203  NV c0, c, a;
204  UV i, n;
205 CODE:
206  if (!SvOK(sv))
207   XSRETURN_UNDEF;
208  n  = SvUV(nsv);
209  c0 = SvNV(csv);
210  if (!n) {
211   ST(0) = sv_2mortal(newSVuv(0));
212   XSRETURN(1);
213  }
214  if (!SvROK(sv) || !c0) {
215   ST(0) = sv;
216   XSRETURN(1);
217  }
218  sv = SvRV(sv);
219  if (!hv_iterinit((HV *) sv))
220   XSRETURN_UNDEF;
221  if (c0 == 1 || (SvIOK(csv) && SvIV(csv) == 1)) {
222   c = n;
223  } else {
224   c = 1;
225   a = c0;
226   for (; n > 0; n /= 2) {
227    if (n % 2)
228     c *= a;
229    a *= a;
230   }
231   c = (1 - c) / (1 - c0);
232  }
233  res = newHV();
234  while (key = hv_iternext((HV *) sv)) {
235   SV *k   = HeSVKEY_force(key);
236   SV *val = newSVnv(c * SvNV(HeVAL(key)));
237   sn_store_ent(res, k, val, 0);
238  }
239  ST(0) = sv_2mortal(newRV_noinc((SV *) res));
240  XSRETURN(1);
241
242 void
243 combine(...)
244 PROTOTYPE: @
245 PREINIT:
246  HV *res[2];
247  SV *cur, *val;
248  SV *list1, *list2;
249  SV *temp;
250  HE *key, *old;
251  I32 i;
252  I32 n = 0, o;
253  I32 j, n1, n2;
254  UV shift = 0, do_shift = 0;
255  sn_combcache *cache = NULL;
256  I32 cachelen = 0;
257 CODE:
258  if (!items)
259   XSRETURN_UNDEF;
260  res[0] = res[1] = NULL;
261  for (i = 0; i < items; ++i) {
262   cur = ST(i);
263   if (!SvOK(cur)) 
264    continue;
265   if (!SvROK(cur)) {
266    if (strEQ(SvPV_nolen(cur), "list")) {
267     res[0] = newHV();
268     n      = 0;
269     sn_store(res[0], "list", 4, newSVuv(1), sn_hash_list);
270     i = items;
271     if (!shift)
272      do_shift = 0;
273     break;
274    } else {
275     shift += SvUV(cur);
276     do_shift = 1;
277     continue;
278    }
279   }
280   cur    = SvRV(cur);
281   res[0] = newHV();
282   while (key = hv_iternext((HV *) cur))
283    sn_store_ent(res[0], HeSVKEY_force(key), newSVsv(HeVAL(key)), 0);
284   n = 0;
285   if (!shift)
286    do_shift = 0;
287   break;
288  }
289  temp = sv_2mortal(newSViv(0));
290  for (++i; i < items; ++i) {
291   cur = ST(i);
292   if (!SvOK(cur))
293    continue;
294   if (!SvROK(cur)) {
295    if (strEQ(SvPV_nolen(cur), "list")) {
296     hv_clear(res[n]);
297     sn_store(res[n], "list", 4, newSVuv(1), sn_hash_list);
298     shift = 0;
299     do_shift = 0;
300     break;
301    } else {
302     shift += SvUV(cur);
303     continue;
304    }
305   }
306   cur = SvRV(cur);
307   o   = 1 - n;
308   if (!res[o])
309    res[o] = newHV();
310   else
311    hv_clear(res[o]);
312   list1 = hv_delete((HV *) cur, "list", 4, 0);
313   n1    = hv_iterinit((HV *) cur);
314   list2 = hv_delete(res[n],     "list", 4, 0);
315   n2    = hv_iterinit(res[n]);
316   if ((list1 && !n1) || (list2 && !n2)) {
317    sn_store(res[o], "list", 4, newSViv(1), sn_hash_list);
318    n = o;
319    break;
320   } else if (list1 || list2) {
321    NV l1 = list1 ? SvNV(list1) : 0;
322    NV l2 = list2 ? SvNV(list2) : 0;
323    val = newSVnv(l1 + l2 - l1 * l2);
324    sn_store(res[o], "list", 4, val, sn_hash_list);
325   }
326   if (n2 > cachelen) {
327    Renew(cache, n2, sn_combcache);
328    cachelen = n2;
329   }
330   j = 0;
331   while (key = hv_iternext(res[n])) {
332    cache[j].k = SvUV(HeSVKEY_force(key));
333    cache[j].v = SvNV(HeVAL(key));
334    ++j;
335   }
336   while (key = hv_iternext((HV *) cur)) {
337    IV k = SvUV(HeSVKEY_force(key));
338    NV v = SvNV(HeVAL(key));
339    for (j = 0; j < n2; ++j) {
340     sv_setiv(temp, k + cache[j].k);
341     if ((old = hv_fetch_ent(res[o], temp, 1, 0)) && SvOK(val = HeVAL(old))) {
342      val = newSVnv(SvNV(val) + v * cache[j].v);
343     } else {
344      val = newSVnv(v * cache[j].v);
345     }
346     sn_store_ent(res[o], temp, val, 0);
347    }
348   }
349   n = o;
350  }
351  Safefree(cache);
352  if (shift || do_shift) {
353   if (!res[n]) {
354    res[n] = newHV();
355    sv_setiv(temp, shift);
356    sn_store_ent(res[n], temp, newSViv(1), 0);
357   } else {
358    o = 1 - n;
359    if (!res[o])
360     res[o] = newHV();
361    else
362     hv_clear(res[o]);
363    list1 = hv_delete(res[n], "list", 4, 0);
364    hv_iterinit(res[n]);
365    while (key = hv_iternext(res[n])) {
366     sv_setiv(temp, SvUV(HeSVKEY_force(key)) + shift);
367     sn_store_ent(res[o], temp, newSVsv(HeVAL(key)), 0);
368    }
369    if (list1)
370     sn_store(res[o], "list", 4, newSVsv(list1), sn_hash_list);
371    n = o;
372   }
373  } else if (!res[0] && !res[1])
374   XSRETURN_UNDEF;
375  if (n == 1)
376   SvREFCNT_dec(res[0]);
377  else if (res[1]) 
378   SvREFCNT_dec(res[1]);
379  ST(0) = sv_2mortal(newRV_noinc((SV *) res[n]));
380  XSRETURN(1);
381
382 void
383 scalops()
384 PROTOTYPE:
385 PREINIT:
386  U32 cxt;
387  int i, count = 0;
388 CODE:
389  cxt = GIMME_V;
390  if (cxt == G_SCALAR) {
391   for (i = 0; i < OP_max; ++i) {
392    count += (PL_opargs[i] & (OA_RETSCALAR | OA_RETINTEGER)) != 0;
393   }
394   EXTEND(SP, 1);
395   mPUSHi(count);
396   XSRETURN(1);
397  } else if (cxt == G_ARRAY) {
398   for (i = 0; i < OP_max; ++i) {
399    if (PL_opargs[i] & (OA_RETSCALAR | OA_RETINTEGER)) {
400     const char *name = PL_op_name[i];
401     XPUSHs(sv_2mortal(newSVpvn_share(name, strlen(name), 0)));
402     ++count;
403    }
404   }
405   XSRETURN(count);
406  }
407