]> git.vpit.fr Git - perl/modules/Sub-Nary.git/blob - Nary.xs
Fix discrepancy between add('list',1) and add({list=>1},1) in favor of the latter
[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  c = 1;
222  a = c0;
223  for (; n > 0; n /= 2) {
224   if (n % 2)
225    c *= a;
226   a *= a;
227  }
228  c = (1 - c) / (1 - c0);
229  res = newHV();
230  while (key = hv_iternext((HV *) sv)) {
231   SV *k   = HeSVKEY_force(key);
232   SV *val = newSVnv(c * SvNV(HeVAL(key)));
233   sn_store_ent(res, k, val, 0);
234  }
235  ST(0) = sv_2mortal(newRV_noinc((SV *) res));
236  XSRETURN(1);
237
238 void
239 combine(...)
240 PROTOTYPE: @
241 PREINIT:
242  HV *res[2];
243  SV *cur, *val;
244  SV *list1, *list2;
245  SV *temp;
246  HE *key, *old;
247  I32 i;
248  I32 n = 0, o;
249  I32 j, n1, n2;
250  UV shift = 0, do_shift = 0;
251  sn_combcache *cache = NULL;
252  I32 cachelen = 0;
253 CODE:
254  if (!items)
255   XSRETURN_UNDEF;
256  res[0] = res[1] = NULL;
257  for (i = 0; i < items; ++i) {
258   cur = ST(i);
259   if (!SvOK(cur)) 
260    continue;
261   if (!SvROK(cur)) {
262    if (strEQ(SvPV_nolen(cur), "list")) {
263     res[0] = newHV();
264     n      = 0;
265     sn_store(res[0], "list", 4, newSVuv(1), sn_hash_list);
266     i = items;
267     if (!shift)
268      do_shift = 0;
269     break;
270    } else {
271     shift += SvUV(cur);
272     do_shift = 1;
273     continue;
274    }
275   }
276   cur    = SvRV(cur);
277   res[0] = newHV();
278   while (key = hv_iternext((HV *) cur))
279    sn_store_ent(res[0], HeSVKEY_force(key), newSVsv(HeVAL(key)), 0);
280   n = 0;
281   if (!shift)
282    do_shift = 0;
283   break;
284  }
285  temp = sv_2mortal(newSViv(0));
286  for (++i; i < items; ++i) {
287   cur = ST(i);
288   if (!SvOK(cur))
289    continue;
290   if (!SvROK(cur)) {
291    if (strEQ(SvPV_nolen(cur), "list")) {
292     hv_clear(res[n]);
293     sn_store(res[n], "list", 4, newSVuv(1), sn_hash_list);
294     shift = 0;
295     do_shift = 0;
296     break;
297    } else {
298     shift += SvUV(cur);
299     continue;
300    }
301   }
302   cur = SvRV(cur);
303   o   = 1 - n;
304   if (!res[o])
305    res[o] = newHV();
306   else
307    hv_clear(res[o]);
308   list1 = hv_delete((HV *) cur, "list", 4, 0);
309   n1    = hv_iterinit((HV *) cur);
310   list2 = hv_delete(res[n],     "list", 4, 0);
311   n2    = hv_iterinit(res[n]);
312   if ((list1 && !n1) || (list2 && !n2)) {
313    sn_store(res[o], "list", 4, newSViv(1), sn_hash_list);
314    n = o;
315    break;
316   } else if (list1 || list2) {
317    NV l1 = list1 ? SvNV(list1) : 0;
318    NV l2 = list2 ? SvNV(list2) : 0;
319    val = newSVnv(l1 + l2 - l1 * l2);
320    sn_store(res[o], "list", 4, val, sn_hash_list);
321   }
322   if (n2 > cachelen) {
323    Renew(cache, n2, sn_combcache);
324    cachelen = n2;
325   }
326   j = 0;
327   while (key = hv_iternext(res[n])) {
328    cache[j].k = SvUV(HeSVKEY_force(key));
329    cache[j].v = SvNV(HeVAL(key));
330    ++j;
331   }
332   while (key = hv_iternext((HV *) cur)) {
333    IV k = SvUV(HeSVKEY_force(key));
334    NV v = SvNV(HeVAL(key));
335    for (j = 0; j < n2; ++j) {
336     sv_setiv(temp, k + cache[j].k);
337     if ((old = hv_fetch_ent(res[o], temp, 1, 0)) && SvOK(val = HeVAL(old))) {
338      val = newSVnv(SvNV(val) + v * cache[j].v);
339     } else {
340      val = newSVnv(v * cache[j].v);
341     }
342     sn_store_ent(res[o], temp, val, 0);
343    }
344   }
345   n = o;
346  }
347  Safefree(cache);
348  if (shift || do_shift) {
349   if (!res[n]) {
350    res[n] = newHV();
351    sv_setiv(temp, shift);
352    sn_store_ent(res[n], temp, newSViv(1), 0);
353   } else {
354    o = 1 - n;
355    if (!res[o])
356     res[o] = newHV();
357    else
358     hv_clear(res[o]);
359    list1 = hv_delete(res[n], "list", 4, 0);
360    hv_iterinit(res[n]);
361    while (key = hv_iternext(res[n])) {
362     sv_setiv(temp, SvUV(HeSVKEY_force(key)) + shift);
363     sn_store_ent(res[o], temp, newSVsv(HeVAL(key)), 0);
364    }
365    if (list1)
366     sn_store(res[o], "list", 4, newSVsv(list1), sn_hash_list);
367    n = o;
368   }
369  } else if (!res[0] && !res[1])
370   XSRETURN_UNDEF;
371  if (n == 1)
372   SvREFCNT_dec(res[0]);
373  else if (res[1]) 
374   SvREFCNT_dec(res[1]);
375  ST(0) = sv_2mortal(newRV_noinc((SV *) res[n]));
376  XSRETURN(1);
377
378 void
379 scalops()
380 PROTOTYPE:
381 PREINIT:
382  U32 cxt;
383  int i, count = 0;
384 CODE:
385  cxt = GIMME_V;
386  if (cxt == G_SCALAR) {
387   for (i = 0; i < OP_max; ++i) {
388    count += (PL_opargs[i] & (OA_RETSCALAR | OA_RETINTEGER)) != 0;
389   }
390   EXTEND(SP, 1);
391   mPUSHi(count);
392   XSRETURN(1);
393  } else if (cxt == G_ARRAY) {
394   for (i = 0; i < OP_max; ++i) {
395    if (PL_opargs[i] & (OA_RETSCALAR | OA_RETINTEGER)) {
396     const char *name = PL_op_name[i];
397     XPUSHs(sv_2mortal(newSVpvn_share(name, strlen(name), 0)));
398     ++count;
399    }
400   }
401   XSRETURN(count);
402  }
403