games_dice 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  *.gem
2
2
  *.rbc
3
3
  .bundle
4
+ *.bundle
4
5
  .config
5
6
  .yardoc
6
7
  Gemfile.lock
@@ -5,10 +5,4 @@ rvm:
5
5
  - "2.0.0"
6
6
  - rbx-18mode
7
7
  - rbx-19mode
8
- - ruby-head
9
8
  - ree
10
- - jruby-18mode
11
- - jruby-19mode
12
- env:
13
- global:
14
- - "JRUBY_OPTS=-Xcext.enabled=true"
data/Rakefile CHANGED
@@ -1,9 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require 'rake/extensiontask'
3
4
  require "yard"
4
5
 
5
- task :default => [:test]
6
-
7
6
  desc "GamesDice unit tests"
8
7
  RSpec::Core::RakeTask.new(:test) do |t|
9
8
  t.pattern = "spec/*_spec.rb"
@@ -13,3 +12,14 @@ end
13
12
  YARD::Rake::YardocTask.new do |t|
14
13
  t.files = ['lib/**/*.rb']
15
14
  end
15
+
16
+ gemspec = Gem::Specification.load('games_dice.gemspec')
17
+ Rake::ExtensionTask.new do |ext|
18
+ ext.name = 'games_dice'
19
+ ext.source_pattern = "*.{c,h}"
20
+ ext.ext_dir = 'ext/games_dice'
21
+ ext.lib_dir = 'lib/games_dice'
22
+ ext.gem_spec = gemspec
23
+ end
24
+
25
+ task :default => [:compile, :test]
@@ -0,0 +1,3 @@
1
+ # ext/games_dice/extconf.rb
2
+ require 'mkmf'
3
+ create_makefile( 'games_dice/games_dice' )
@@ -0,0 +1,12 @@
1
+ // ext/games_dice/games_dice.c
2
+
3
+ #include <ruby.h>
4
+ #include "probabilities.h"
5
+
6
+ // To hold the module object
7
+ VALUE GamesDice = Qnil;
8
+
9
+ void Init_games_dice() {
10
+ GamesDice = rb_define_module("GamesDice");
11
+ init_probabilities_class( GamesDice );
12
+ }
@@ -0,0 +1,765 @@
1
+ // ext/games_dice/probabilities.c
2
+
3
+ #include "probabilities.h"
4
+
5
+ // Ruby 1.8.7 compatibility patch
6
+ #ifndef DBL2NUM
7
+ #define DBL2NUM( dbl_val ) rb_float_new( dbl_val )
8
+ #endif
9
+
10
+ VALUE Probabilities = Qnil;
11
+
12
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
13
+ //
14
+ // General utils
15
+ //
16
+
17
+ static inline int max( int *a, int n ) {
18
+ int m = -1000000000;
19
+ int i;
20
+ for ( i=0; i < n; i++ ) {
21
+ m = a[i] > m ? a[i] : m;
22
+ }
23
+ return m;
24
+ }
25
+
26
+ static inline int min( int *a, int n ) {
27
+ int m = 1000000000;
28
+ int i;
29
+ for ( i=0; i < n; i++ ) {
30
+ m = a[i] < m ? a[i] : m;
31
+ }
32
+ return m;
33
+ }
34
+
35
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
36
+ //
37
+ // Quick factorials, that fit into unsigned longs . . . the size of this structure sets the
38
+ // maximum possible n in repeat_n_sum_k calculations
39
+ //
40
+
41
+ // There is no point calculating these, a cache of them is just fine.
42
+ static double nfact[171] = {
43
+ 1.0, 1.0, 2.0, 6.0,
44
+ 24.0, 120.0, 720.0, 5040.0,
45
+ 40320.0, 362880.0, 3628800.0, 39916800.0,
46
+ 479001600.0, 6227020800.0, 87178291200.0, 1307674368000.0,
47
+ 20922789888000.0, 355687428096000.0, 6402373705728000.0, 121645100408832000.0,
48
+ 2432902008176640000.0, 51090942171709440000.0, 1124000727777607700000.0, 25852016738884980000000.0,
49
+ 620448401733239400000000.0, 15511210043330986000000000.0, 403291461126605650000000000.0, 10888869450418352000000000000.0,
50
+ 304888344611713870000000000000.0, 8841761993739702000000000000000.0, 2.6525285981219107e+32, 8.222838654177922e+33,
51
+ 2.631308369336935e+35, 8.683317618811886e+36, 2.9523279903960416e+38, 1.0333147966386145e+40,
52
+ 3.7199332678990125e+41, 1.3763753091226346e+43, 5.230226174666011e+44, 2.0397882081197444e+46,
53
+ 8.159152832478977e+47, 3.345252661316381e+49, 1.40500611775288e+51, 6.041526306337383e+52,
54
+ 2.658271574788449e+54, 1.1962222086548019e+56, 5.502622159812089e+57, 2.5862324151116818e+59,
55
+ 1.2413915592536073e+61, 6.082818640342675e+62, 3.0414093201713376e+64, 1.5511187532873822e+66,
56
+ 8.065817517094388e+67, 4.2748832840600255e+69, 2.308436973392414e+71, 1.2696403353658276e+73,
57
+ 7.109985878048635e+74, 4.0526919504877214e+76, 2.3505613312828785e+78, 1.3868311854568984e+80,
58
+ 8.32098711274139e+81, 5.075802138772248e+83, 3.146997326038794e+85, 1.98260831540444e+87,
59
+ 1.2688693218588417e+89, 8.247650592082472e+90, 5.443449390774431e+92, 3.647111091818868e+94,
60
+ 2.4800355424368305e+96, 1.711224524281413e+98, 1.1978571669969892e+100, 8.504785885678623e+101,
61
+ 6.1234458376886085e+103, 4.4701154615126844e+105, 3.307885441519386e+107, 2.48091408113954e+109,
62
+ 1.8854947016660504e+111, 1.4518309202828587e+113, 1.1324281178206297e+115, 8.946182130782976e+116,
63
+ 7.156945704626381e+118, 5.797126020747368e+120, 4.753643337012842e+122, 3.945523969720659e+124,
64
+ 3.314240134565353e+126, 2.81710411438055e+128, 2.4227095383672734e+130, 2.107757298379528e+132,
65
+ 1.8548264225739844e+134, 1.650795516090846e+136, 1.4857159644817615e+138, 1.352001527678403e+140,
66
+ 1.2438414054641308e+142, 1.1567725070816416e+144, 1.087366156656743e+146, 1.032997848823906e+148,
67
+ 9.916779348709496e+149, 9.619275968248212e+151, 9.426890448883248e+153, 9.332621544394415e+155,
68
+ 9.332621544394415e+157, 9.42594775983836e+159, 9.614466715035127e+161, 9.90290071648618e+163,
69
+ 1.0299016745145628e+166, 1.081396758240291e+168, 1.1462805637347084e+170, 1.226520203196138e+172,
70
+ 1.324641819451829e+174, 1.4438595832024937e+176, 1.588245541522743e+178, 1.7629525510902446e+180,
71
+ 1.974506857221074e+182, 2.2311927486598138e+184, 2.5435597334721877e+186, 2.925093693493016e+188,
72
+ 3.393108684451898e+190, 3.969937160808721e+192, 4.684525849754291e+194, 5.574585761207606e+196,
73
+ 6.689502913449127e+198, 8.094298525273444e+200, 9.875044200833601e+202, 1.214630436702533e+205,
74
+ 1.506141741511141e+207, 1.882677176888926e+209, 2.372173242880047e+211, 3.0126600184576594e+213,
75
+ 3.856204823625804e+215, 4.974504222477287e+217, 6.466855489220474e+219, 8.47158069087882e+221,
76
+ 1.1182486511960043e+224, 1.4872707060906857e+226, 1.9929427461615188e+228, 2.6904727073180504e+230,
77
+ 3.659042881952549e+232, 5.012888748274992e+234, 6.917786472619489e+236, 9.615723196941089e+238,
78
+ 1.3462012475717526e+241, 1.898143759076171e+243, 2.695364137888163e+245, 3.854370717180073e+247,
79
+ 5.5502938327393044e+249, 8.047926057471992e+251, 1.1749972043909107e+254, 1.727245890454639e+256,
80
+ 2.5563239178728654e+258, 3.80892263763057e+260, 5.713383956445855e+262, 8.62720977423324e+264,
81
+ 1.3113358856834524e+267, 2.0063439050956823e+269, 3.0897696138473508e+271, 4.789142901463394e+273,
82
+ 7.471062926282894e+275, 1.1729568794264145e+278, 1.853271869493735e+280, 2.9467022724950384e+282,
83
+ 4.7147236359920616e+284, 7.590705053947219e+286, 1.2296942187394494e+289, 2.0044015765453026e+291,
84
+ 3.287218585534296e+293, 5.423910666131589e+295, 9.003691705778438e+297, 1.503616514864999e+300,
85
+ 2.5260757449731984e+302, 4.269068009004705e+304, 7.257415615307999e+306 };
86
+
87
+ static double num_arrangements( int *args, int nargs ) {
88
+ int sum = 0;
89
+ double div_by = 1.0;
90
+ int i;
91
+ for ( i = 0; i < nargs; i++ ) {
92
+ sum += args[i];
93
+ if ( sum > 170 ) {
94
+ rb_raise( rb_eRuntimeError, "Too many dice to calculate numbers of arrangements" );
95
+ }
96
+ div_by *= nfact[ args[i] ];
97
+ }
98
+ return nfact[ sum ] / div_by;
99
+ }
100
+
101
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
102
+ //
103
+ // Probability List basics - create, delete, copy
104
+ //
105
+
106
+ static ProbabilityList *create_probability_list() {
107
+ ProbabilityList *pl;
108
+ pl = malloc (sizeof(ProbabilityList));
109
+ if ( pl == NULL ) {
110
+ rb_raise(rb_eRuntimeError, "Could not allocate memory for Probabilities");
111
+ }
112
+ pl->probs = NULL;
113
+ pl->cumulative = NULL;
114
+ pl->slots = 0;
115
+ pl->offset = 0;
116
+ return pl;
117
+ }
118
+
119
+ static void destroy_probability_list( ProbabilityList *pl ) {
120
+ xfree( pl->cumulative );
121
+ xfree( pl->probs );
122
+ xfree( pl );
123
+ return;
124
+ }
125
+
126
+ static double *alloc_probs( ProbabilityList *pl, int slots ) {
127
+ if ( slots < 1 || slots > 1000000 ) {
128
+ rb_raise(rb_eArgError, "Bad number of probability slots");
129
+ }
130
+ pl->slots = slots;
131
+
132
+ double *pr = ALLOC_N( double, slots );
133
+ pl->probs = pr;
134
+
135
+ pl->cumulative = ALLOC_N( double, slots );
136
+
137
+ return pr;
138
+ }
139
+
140
+ static double calc_cumulative( ProbabilityList *pl ) {
141
+ double *c = pl->cumulative;
142
+ double *pr = pl->probs;
143
+ int i;
144
+ double t = 0.0;
145
+ for(i=0; i < pl->slots; i++) {
146
+ t += pr[i];
147
+ c[i] = t;
148
+ }
149
+ return t;
150
+ }
151
+
152
+ static double *alloc_probs_iv( ProbabilityList *pl, int slots, double iv ) {
153
+ if ( iv < 0.0 || iv > 1.0 ) {
154
+ rb_raise(rb_eArgError, "Bad single probability value");
155
+ }
156
+ double *pr = alloc_probs( pl, slots );
157
+ int i;
158
+ for(i=0; i<slots; i++) {
159
+ pr[i] = iv;
160
+ }
161
+ calc_cumulative( pl );
162
+ return pr;
163
+ }
164
+
165
+ static ProbabilityList *copy_probability_list( ProbabilityList *orig ) {
166
+ ProbabilityList *pl = create_probability_list();
167
+ double *pr = alloc_probs( pl, orig->slots );
168
+ pl->offset = orig->offset;
169
+ memcpy( pr, orig->probs, orig->slots * sizeof(double) );
170
+ memcpy( pl->cumulative, orig->cumulative, orig->slots * sizeof(double) );
171
+ return pl;
172
+ }
173
+
174
+ static inline ProbabilityList *new_basic_pl( int nslots, double iv, int o ) {
175
+ ProbabilityList *pl = create_probability_list();
176
+ alloc_probs_iv( pl, nslots, iv );
177
+ pl->offset = o;
178
+ return pl;
179
+ }
180
+
181
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
182
+ //
183
+ // Probability List core "native" methods
184
+ //
185
+
186
+ static inline int pl_min( ProbabilityList *pl ) {
187
+ return pl->offset;
188
+ }
189
+
190
+ static inline int pl_max( ProbabilityList *pl ) {
191
+ return pl->offset + pl->slots - 1;
192
+ }
193
+
194
+ static ProbabilityList *pl_add_distributions( ProbabilityList *pl_a, ProbabilityList *pl_b ) {
195
+ int s = pl_a->slots + pl_b->slots - 1;
196
+ int o = pl_a->offset + pl_b->offset;
197
+ int i,j;
198
+
199
+ ProbabilityList *pl = create_probability_list();
200
+ pl->offset = o;
201
+ double *pr = alloc_probs_iv( pl, s, 0.0 );
202
+ for ( i=0; i < pl_a->slots; i++ ) { for ( j=0; j < pl_b->slots; j++ ) {
203
+ pr[ i + j ] += (pl_a->probs)[i] * (pl_b->probs)[j];
204
+ } }
205
+ calc_cumulative( pl );
206
+ return pl;
207
+ }
208
+
209
+ static ProbabilityList *pl_add_distributions_mult( int mul_a, ProbabilityList *pl_a, int mul_b, ProbabilityList *pl_b ) {
210
+ int pts[4] = {
211
+ mul_a * pl_min( pl_a ) + mul_b * pl_min( pl_b ),
212
+ mul_a * pl_max( pl_a ) + mul_b * pl_min( pl_b ),
213
+ mul_a * pl_min( pl_a ) + mul_b * pl_max( pl_b ),
214
+ mul_a * pl_max( pl_a ) + mul_b * pl_max( pl_b ) };
215
+
216
+ int combined_min = min( pts, 4 );
217
+ int combined_max = max( pts, 4 );
218
+ int s = 1 + combined_max - combined_min;
219
+
220
+ ProbabilityList *pl = create_probability_list();
221
+ pl->offset = combined_min;
222
+ double *pr = alloc_probs_iv( pl, s, 0.0 );
223
+ int i,j;
224
+ for ( i=0; i < pl_a->slots; i++ ) { for ( j=0; j < pl_b->slots; j++ ) {
225
+ int k = mul_a * (i + pl_a->offset) + mul_b * (j + pl_b->offset) - combined_min;
226
+ pr[ i + j ] += (pl_a->probs)[i] * (pl_b->probs)[j];
227
+ } }
228
+ calc_cumulative( pl );
229
+ return pl;
230
+ }
231
+
232
+ static inline double pl_p_eql( ProbabilityList *pl, int target ) {
233
+ int idx = target - pl->offset;
234
+ if ( idx < 0 || idx >= pl->slots ) {
235
+ return 0.0;
236
+ }
237
+ return (pl->probs)[idx];
238
+ }
239
+
240
+ static inline double pl_p_gt( ProbabilityList *pl, int target ) {
241
+ return 1.0 - pl_p_le( pl, target );
242
+ }
243
+
244
+ static inline double pl_p_lt( ProbabilityList *pl, int target ) {
245
+ return pl_p_le( pl, target - 1 );
246
+ }
247
+
248
+ static inline double pl_p_le( ProbabilityList *pl, int target ) {
249
+ int idx = target - pl->offset;
250
+ if ( idx < 0 ) {
251
+ return 0.0;
252
+ }
253
+ if ( idx >= pl->slots - 1 ) {
254
+ return 1.0;
255
+ }
256
+ return (pl->cumulative)[idx];
257
+ }
258
+
259
+ static inline double pl_p_ge( ProbabilityList *pl, int target ) {
260
+ return 1.0 - pl_p_le( pl, target - 1 );
261
+ }
262
+
263
+ static inline double pl_expected( ProbabilityList *pl ) {
264
+ double t = 0.0;
265
+ int o = pl->offset;
266
+ int s = pl->slots;
267
+ double *pr = pl->probs;
268
+ int i;
269
+ for ( i = 0; i < s ; i++ ) {
270
+ t += ( i + o ) * pr[i];
271
+ }
272
+ return t;
273
+ }
274
+
275
+ static ProbabilityList *pl_given_ge( ProbabilityList *pl, int target ) {
276
+ int m = pl_min( pl );
277
+ if ( m > target ) {
278
+ target = m;
279
+ }
280
+ double p = pl_p_ge( pl, target );
281
+ if ( p <= 0.0 ) {
282
+ rb_raise( rb_eRuntimeError, "Cannot calculate given probabilities, divide by zero" );
283
+ }
284
+ double mult = 1.0/p;
285
+ int s = pl->slots + pl->offset - target;
286
+ double *pr = pl->probs;
287
+
288
+ ProbabilityList *new_pl = create_probability_list();
289
+ new_pl->offset = target;
290
+ double *new_pr = alloc_probs( new_pl, s );
291
+ int o = target - pl->offset;
292
+ int i;
293
+ for ( i = 0; i < s; i++ ) {
294
+ new_pr[i] = pr[o + i] * mult;
295
+ }
296
+ calc_cumulative( new_pl );
297
+ return new_pl;
298
+ }
299
+
300
+ static ProbabilityList *pl_given_le( ProbabilityList *pl, int target ) {
301
+ int m = pl_max( pl );
302
+ if ( m < target ) {
303
+ target = m;
304
+ }
305
+ double p = pl_p_le( pl, target );
306
+ if ( p <= 0.0 ) {
307
+ rb_raise( rb_eRuntimeError, "Cannot calculate given probabilities, divide by zero" );
308
+ }
309
+ double mult = 1.0/p;
310
+ int s = target - pl->offset + 1;
311
+ double *pr = pl->probs;
312
+
313
+ ProbabilityList *new_pl = create_probability_list();
314
+ new_pl->offset = pl->offset;
315
+ double *new_pr = alloc_probs( new_pl, s );
316
+ int i;
317
+ for ( i = 0; i < s; i++ ) {
318
+ new_pr[i] = pr[i] * mult;
319
+ }
320
+ calc_cumulative( new_pl );
321
+ return new_pl;
322
+ }
323
+
324
+ static ProbabilityList *pl_repeat_sum( ProbabilityList *pl, int n ) {
325
+ if ( n < 1 ) {
326
+ rb_raise( rb_eRuntimeError, "Cannot calculate repeat_sum when n < 1" );
327
+ }
328
+ if ( n * pl->slots - n > 1000000 ) {
329
+ rb_raise( rb_eRuntimeError, "Too many probability slots" );
330
+ }
331
+
332
+ ProbabilityList *pd_power = copy_probability_list( pl );
333
+ ProbabilityList *pd_result = NULL;
334
+ ProbabilityList *pd_next = NULL;
335
+ int power = 1;
336
+ while ( 1 ) {
337
+ if ( power & n ) {
338
+ if ( pd_result ) {
339
+ pd_next = pl_add_distributions( pd_result, pd_power );
340
+ destroy_probability_list( pd_result );
341
+ pd_result = pd_next;
342
+ } else {
343
+ pd_result = copy_probability_list( pd_power );
344
+ }
345
+ }
346
+ power = power << 1;
347
+ if ( power > n ) break;
348
+ pd_next = pl_add_distributions( pd_power, pd_power );
349
+ destroy_probability_list( pd_power );
350
+ pd_power = pd_next;
351
+ }
352
+ destroy_probability_list( pd_power );
353
+
354
+ return pd_result;
355
+ }
356
+
357
+ // Assigns { p_rejected, p_maybe, p_kept } to buffer
358
+ static void calc_p_table( ProbabilityList *pl, int q, int kbest, double *buffer ) {
359
+ if ( kbest ) {
360
+ buffer[2] = pl_p_gt( pl, q );
361
+ buffer[1] = pl_p_eql( pl, q );
362
+ buffer[0] = pl_p_lt( pl, q );
363
+ } else {
364
+ buffer[2] = pl_p_lt( pl, q );
365
+ buffer[1] = pl_p_eql( pl, q );
366
+ buffer[0] = pl_p_gt( pl, q );
367
+ }
368
+ return;
369
+ }
370
+
371
+ // Assigns a list of pl variants to a buffer
372
+ static void calc_keep_distributions( ProbabilityList *pl, int k, int q, int kbest, ProbabilityList **pl_array ) {
373
+ // Init array
374
+ int n;
375
+ for ( n=0; n<k; n++) { pl_array[n] = NULL; }
376
+ pl_array[0] = new_basic_pl( 1, 1.0, q * k );
377
+ ProbabilityList *pl_kd;
378
+
379
+ if ( kbest ) {
380
+ if ( pl_p_gt( pl, q ) > 0.0 && k > 1 ) {
381
+ pl_kd = pl_given_ge( pl, q + 1 );
382
+ for ( n = 1; n < k; n++ ) {
383
+ pl_array[n] = pl_repeat_sum( pl_kd, n );
384
+ (pl_array[n])->offset += q * ( k - n );
385
+ }
386
+ }
387
+ } else {
388
+ if ( pl_p_lt( pl, q ) > 0.0 && k > 1 ) {
389
+ pl_kd = pl_given_le( pl, q - 1 );
390
+ for ( n = 1; n < k; n++ ) {
391
+ pl_array[n] = pl_repeat_sum( pl_kd, n );
392
+ (pl_array[n])->offset += q * ( k - n );
393
+ }
394
+ }
395
+ }
396
+
397
+ return;
398
+ }
399
+
400
+ static inline void clear_pl_array( int k, ProbabilityList **pl_array ) {
401
+ int n;
402
+ for ( n=0; n<k; n++) {
403
+ if ( pl_array[n] != NULL ) {
404
+ destroy_probability_list( pl_array[n] );
405
+ }
406
+ }
407
+ return;
408
+ }
409
+
410
+ static ProbabilityList *pl_repeat_n_sum_k( ProbabilityList *pl, int n, int k, int kbest ) {
411
+ if ( n < 1 ) {
412
+ rb_raise( rb_eRuntimeError, "Cannot calculate repeat_n_sum_k when n < 1" );
413
+ }
414
+ if ( k < 1 ) {
415
+ rb_raise( rb_eRuntimeError, "Cannot calculate repeat_sum_k when k < 1" );
416
+ }
417
+ if ( k >= n ) {
418
+ return pl_repeat_sum( pl, n );
419
+ }
420
+ if ( k * pl->slots - k >= 1000000 ) {
421
+ rb_raise( rb_eRuntimeError, "Too many probability slots" );
422
+ }
423
+ if ( n > 170 ) {
424
+ rb_raise( rb_eRuntimeError, "Too many dice to calculate combinations" );
425
+ }
426
+
427
+ // Init target
428
+ ProbabilityList *pl_result = create_probability_list();
429
+ double *pr = alloc_probs_iv( pl_result, 1 + k * (pl->slots - 1), 0.0 );
430
+ pl_result->offset = pl->offset * k;
431
+
432
+ // Table of probabilities ( reject, maybe, keep ) for each "pivot point"
433
+ double p_table[3];
434
+ int keep_combos[3];
435
+ // Table of distributions for each count of > pivot point (vs == pivot point)
436
+ ProbabilityList *keep_distributions[171];
437
+ ProbabilityList *kd;
438
+
439
+ int d = n - k;
440
+ int i, j, q, dn, kn, mn, kdq;
441
+ double p_sequence;
442
+
443
+ for ( i = 0; i < pl->slots; i++ ) {
444
+ if ( ! pl->probs[i] > 0.0 ) continue;
445
+
446
+ q = i + pl->offset;
447
+ calc_keep_distributions( pl, k, q, kbest, keep_distributions );
448
+ calc_p_table( pl, q, kbest, p_table );
449
+
450
+ for ( kn = 0; kn < k; kn++ ) {
451
+ // Construct keepers. maybes, discards (just counts of these) . . .
452
+ if ( kn > 0 && ! ( p_table[2] > 0.0 ) ) continue;
453
+
454
+ for ( dn = 0; dn <= d; dn++ ) {
455
+ mn = (k - kn) + ( d - dn );
456
+ if ( dn > 0 && ! ( p_table[0] > 0.0 ) ) continue;
457
+ p_sequence = 1.0;
458
+ for ( j = 0; j < dn; j++ ) { p_sequence *= p_table[0]; }
459
+ for ( j = 0; j < mn; j++ ) { p_sequence *= p_table[1]; }
460
+ for ( j = 0; j < kn; j++ ) { p_sequence *= p_table[2]; }
461
+ keep_combos[0] = dn;
462
+ keep_combos[1] = mn;
463
+ keep_combos[2] = kn;
464
+ p_sequence *= num_arrangements( keep_combos, 3 );
465
+ kd = keep_distributions[ kn ];
466
+
467
+ for ( j = 0; j < kd->slots; j++ ) {
468
+ kdq = j + kd->offset;
469
+ pr[ kdq - pl_result->offset ] += p_sequence * kd->probs[ j ];
470
+ }
471
+ }
472
+ }
473
+ clear_pl_array( k, keep_distributions );
474
+ }
475
+
476
+ calc_cumulative( pl_result );
477
+ return pl_result;
478
+ }
479
+
480
+
481
+
482
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
483
+ //
484
+ // Ruby integration
485
+ //
486
+
487
+ static inline VALUE pl_as_ruby_class( ProbabilityList *pl, VALUE klass ) {
488
+ return Data_Wrap_Struct( klass, 0, destroy_probability_list, pl );
489
+ }
490
+
491
+ static VALUE pl_alloc(VALUE klass) {
492
+ return pl_as_ruby_class( create_probability_list(), klass );
493
+ }
494
+
495
+ inline static ProbabilityList *get_probability_list( VALUE obj ) {
496
+ ProbabilityList *pl;
497
+ Data_Get_Struct( obj, ProbabilityList, pl );
498
+ return pl;
499
+ }
500
+
501
+ static void assert_value_wraps_pl( VALUE obj ) {
502
+ if ( TYPE(obj) != T_DATA ||
503
+ RDATA(obj)->dfree != (RUBY_DATA_FUNC)destroy_probability_list) {
504
+ rb_raise( rb_eTypeError, "Expected a Probabilities object, but got something else" );
505
+ }
506
+ }
507
+
508
+ // Validate key/value from hash, and adjust object properties as required
509
+ int validate_key_value( VALUE key, VALUE val, VALUE obj ) {
510
+ int k = NUM2INT( key );
511
+ double v = NUM2DBL( val );
512
+ ProbabilityList *pl = get_probability_list( obj );
513
+ if ( k < pl->offset ) {
514
+ if ( pl->slots < 1 ) {
515
+ pl->slots = 1;
516
+ } else {
517
+ pl->slots = pl->slots - k + pl->offset;
518
+ }
519
+ pl->offset = k;
520
+ } else if ( k - pl->offset >= pl->slots ) {
521
+ pl->slots = 1 + k - pl->offset;
522
+ }
523
+ return ST_CONTINUE;
524
+ }
525
+
526
+ // Copy key/value from hash
527
+ int copy_key_value( VALUE key, VALUE val, VALUE obj ) {
528
+ int k = NUM2INT( key );
529
+ double v = NUM2DBL( val );
530
+ ProbabilityList *pl = get_probability_list( obj );
531
+ pl->probs[ k - pl->offset ] = v;
532
+ return ST_CONTINUE;
533
+ }
534
+
535
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
536
+ //
537
+ // Ruby class and instance methods for Probabilities
538
+ //
539
+
540
+ static VALUE probabilities_initialize( VALUE self, VALUE arr, VALUE offset ) {
541
+ int o = NUM2INT(offset);
542
+ Check_Type( arr, T_ARRAY );
543
+ int s = FIX2INT( rb_funcall( arr, rb_intern("count"), 0 ) );
544
+ ProbabilityList *pl = get_probability_list( self );
545
+ pl->offset = o;
546
+ int i;
547
+ double *pr = alloc_probs( pl, s );
548
+ for(i=0; i<s; i++) {
549
+ double p_item = NUM2DBL( rb_ary_entry( arr, i ) );
550
+ if ( p_item < 0.0 ) {
551
+ rb_raise( rb_eArgError, "Negative probability not allowed" );
552
+ } else if ( p_item > 1.0 ) {
553
+ rb_raise( rb_eArgError, "Probability must be in range 0.0..1.0" );
554
+ }
555
+ pr[i] = p_item;
556
+ }
557
+ double error = calc_cumulative( pl ) - 1.0;
558
+ if ( error < -1.0e-8 ) {
559
+ rb_raise( rb_eArgError, "Total probabilities are less than 1.0" );
560
+ } else if ( error > 1.0e-8 ) {
561
+ rb_raise( rb_eArgError, "Total probabilities are greater than 1.0" );
562
+ }
563
+ return self;
564
+ }
565
+
566
+
567
+ static VALUE probabilities_initialize_copy( VALUE copy, VALUE orig ) {
568
+ if (copy == orig) return copy;
569
+ ProbabilityList *pl_copy = get_probability_list( copy );
570
+ ProbabilityList *pl_orig = get_probability_list( orig );
571
+
572
+ double *pr = alloc_probs( pl_copy, pl_orig->slots );
573
+ pl_copy->offset = pl_orig->offset;
574
+ memcpy( pr, pl_orig->probs, pl_orig->slots * sizeof(double) );
575
+ memcpy( pl_copy->cumulative, pl_orig->cumulative, pl_orig->slots * sizeof(double) );;
576
+
577
+ return copy;
578
+ }
579
+
580
+ VALUE probabilities_to_h( VALUE self ) {
581
+ ProbabilityList *pl = get_probability_list( self );
582
+ VALUE h = rb_hash_new();
583
+ double *pr = pl->probs;
584
+ int s = pl->slots;
585
+ int o = pl->offset;
586
+ int i;
587
+ for(i=0; i<s; i++) {
588
+ if ( pr[i] > 0.0 ) {
589
+ rb_hash_aset( h, INT2FIX( o + i ), DBL2NUM( pr[i] ) );
590
+ }
591
+ }
592
+ return h;
593
+ }
594
+
595
+ VALUE probabilities_min( VALUE self ) {
596
+ return INT2NUM( pl_min( get_probability_list( self ) ) );
597
+ }
598
+
599
+ VALUE probabilities_max( VALUE self ) {
600
+ return INT2NUM( pl_max( get_probability_list( self ) ) );
601
+ }
602
+
603
+ VALUE probabilites_p_eql( VALUE self, VALUE target ) {
604
+ return DBL2NUM( pl_p_eql( get_probability_list( self ), NUM2INT(target) ) );
605
+ }
606
+
607
+ VALUE probabilites_p_gt( VALUE self, VALUE target ) {
608
+ return DBL2NUM( pl_p_gt( get_probability_list( self ), NUM2INT(target) ) );
609
+ }
610
+
611
+ VALUE probabilites_p_ge( VALUE self, VALUE target ) {
612
+ return DBL2NUM( pl_p_ge( get_probability_list( self ), NUM2INT(target) ) );
613
+ }
614
+
615
+ VALUE probabilites_p_le( VALUE self, VALUE target ) {
616
+ return DBL2NUM( pl_p_le( get_probability_list( self ), NUM2INT(target) ) );
617
+ }
618
+
619
+ VALUE probabilites_p_lt( VALUE self, VALUE target ) {
620
+ return DBL2NUM( pl_p_lt( get_probability_list( self ), NUM2INT(target) ) );
621
+ }
622
+
623
+ VALUE probabilites_expected( VALUE self ) {
624
+ return DBL2NUM( pl_expected( get_probability_list( self ) ) );
625
+ }
626
+
627
+ VALUE probabilities_given_ge( VALUE self, VALUE target ) {
628
+ int t = NUM2INT(target);
629
+ ProbabilityList *pl = get_probability_list( self );
630
+ return pl_as_ruby_class( pl_given_ge( pl, t ), Probabilities );
631
+ }
632
+
633
+ VALUE probabilities_given_le( VALUE self, VALUE target ) {
634
+ int t = NUM2INT(target);
635
+ ProbabilityList *pl = get_probability_list( self );
636
+ return pl_as_ruby_class( pl_given_le( pl, t ), Probabilities );
637
+ }
638
+
639
+ VALUE probabilities_repeat_sum( VALUE self, VALUE nsum ) {
640
+ int n = NUM2INT(nsum);
641
+ ProbabilityList *pl = get_probability_list( self );
642
+ return pl_as_ruby_class( pl_repeat_sum( pl, n ), Probabilities );
643
+ }
644
+
645
+ static VALUE probabilities_repeat_n_sum_k( int argc, VALUE* argv, VALUE self ) {
646
+ VALUE nsum, nkeepers, kmode;
647
+ rb_scan_args( argc, argv, "21", &nsum, &nkeepers, &kmode );
648
+ int keep_best = 1;
649
+ if (NIL_P(kmode)) {
650
+ keep_best = 1;
651
+ } else if ( rb_intern("keep_worst") == SYM2ID(kmode) ) {
652
+ keep_best = 0;
653
+ } else if ( rb_intern("keep_best") != SYM2ID(kmode) ) {
654
+ rb_raise( rb_eArgError, "Keep mode not recognised" );
655
+ }
656
+
657
+ int n = NUM2INT(nsum);
658
+ int k = NUM2INT(nkeepers);
659
+ ProbabilityList *pl = get_probability_list( self );
660
+ return pl_as_ruby_class( pl_repeat_n_sum_k( pl, n, k, keep_best ), Probabilities );
661
+ }
662
+
663
+ VALUE probabilities_each( VALUE self ) {
664
+ ProbabilityList *pl = get_probability_list( self );
665
+ int i;
666
+ double *pr = pl->probs;
667
+ int o = pl->offset;
668
+ for ( i = 0; i < pl->slots; i++ ) {
669
+ if ( pr[i] > 0.0 ) {
670
+ VALUE a = rb_ary_new2( 2 );
671
+ rb_ary_store( a, 0, INT2NUM( i + o ));
672
+ rb_ary_store( a, 1, DBL2NUM( pr[i] ));
673
+ rb_yield( a );
674
+ }
675
+ }
676
+ return self;
677
+ }
678
+
679
+ VALUE probabilities_for_fair_die( VALUE self, VALUE sides ) {
680
+ int s = NUM2INT( sides );
681
+ if ( s < 1 ) {
682
+ rb_raise( rb_eArgError, "Number of sides should be 1 or more" );
683
+ }
684
+ if ( s > 100000 ) {
685
+ rb_raise( rb_eArgError, "Number of sides should be less than 100001" );
686
+ }
687
+ VALUE obj = pl_alloc( Probabilities );
688
+ ProbabilityList *pl = get_probability_list( obj );
689
+ pl->offset = 1;
690
+ alloc_probs_iv( pl, s, 1.0/s );
691
+ return obj;
692
+ }
693
+
694
+ VALUE probabilities_from_h( VALUE self, VALUE hash ) {
695
+ rb_check_hash_type( hash );
696
+
697
+ VALUE obj = pl_alloc( Probabilities );
698
+ ProbabilityList *pl = get_probability_list( obj );
699
+ // Set these up so that they get adjusted during hash iteration
700
+ pl->offset = 2000000000;
701
+ pl->slots = 0;
702
+ // First iteration establish min/max and validate all key/values
703
+ rb_hash_foreach( hash, validate_key_value, obj );
704
+
705
+ double *pr = alloc_probs_iv( pl, pl->slots, 0.0 );
706
+ // Second iteration copy key/value pairs into structure
707
+ rb_hash_foreach( hash, copy_key_value, obj );
708
+
709
+ double error = calc_cumulative( pl ) - 1.0;
710
+ if ( error < -1.0e-8 ) {
711
+ rb_raise( rb_eArgError, "Total probabilities are less than 1.0" );
712
+ } else if ( error > 1.0e-8 ) {
713
+ rb_raise( rb_eArgError, "Total probabilities are greater than 1.0" );
714
+ }
715
+ return obj;
716
+ }
717
+
718
+ VALUE probabilities_add_distributions( VALUE self, VALUE gdpa, VALUE gdpb ) {
719
+ assert_value_wraps_pl( gdpa );
720
+ assert_value_wraps_pl( gdpb );
721
+ ProbabilityList *pl_a = get_probability_list( gdpa );
722
+ ProbabilityList *pl_b = get_probability_list( gdpb );
723
+ return pl_as_ruby_class( pl_add_distributions( pl_a, pl_b ), Probabilities );
724
+ }
725
+
726
+ VALUE probabilities_add_distributions_mult( VALUE self, VALUE m_a, VALUE gdpa, VALUE m_b, VALUE gdpb ) {
727
+ assert_value_wraps_pl( gdpa );
728
+ assert_value_wraps_pl( gdpb );
729
+ int mul_a = NUM2INT( m_a );
730
+ ProbabilityList *pl_a = get_probability_list( gdpa );
731
+ int mul_b = NUM2INT( m_b );
732
+ ProbabilityList *pl_b = get_probability_list( gdpb );
733
+ return pl_as_ruby_class( pl_add_distributions_mult( mul_a, pl_a, mul_b, pl_b ), Probabilities );
734
+ }
735
+
736
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
737
+ //
738
+ // Setup Probabilities class for Ruby interpretter
739
+ //
740
+
741
+ void init_probabilities_class( VALUE ParentModule ) {
742
+ Probabilities = rb_define_class_under( ParentModule, "Probabilities", rb_cObject );
743
+ rb_define_alloc_func( Probabilities, pl_alloc );
744
+ rb_define_method( Probabilities, "initialize", probabilities_initialize, 2 );
745
+ rb_define_method( Probabilities, "initialize_copy", probabilities_initialize_copy, 1 );
746
+ rb_define_method( Probabilities, "to_h", probabilities_to_h, 0 );
747
+ rb_define_method( Probabilities, "min", probabilities_min, 0 );
748
+ rb_define_method( Probabilities, "max", probabilities_max, 0 );
749
+ rb_define_method( Probabilities, "p_eql", probabilites_p_eql, 1 );
750
+ rb_define_method( Probabilities, "p_gt", probabilites_p_gt, 1 );
751
+ rb_define_method( Probabilities, "p_ge", probabilites_p_ge, 1 );
752
+ rb_define_method( Probabilities, "p_le", probabilites_p_le, 1 );
753
+ rb_define_method( Probabilities, "p_lt", probabilites_p_lt, 1 );
754
+ rb_define_method( Probabilities, "expected", probabilites_expected, 0 );
755
+ rb_define_method( Probabilities, "each", probabilities_each, 0 );
756
+ rb_define_method( Probabilities, "given_ge", probabilities_given_ge, 1 );
757
+ rb_define_method( Probabilities, "given_le", probabilities_given_le, 1 );
758
+ rb_define_method( Probabilities, "repeat_sum", probabilities_repeat_sum, 1 );
759
+ rb_define_method( Probabilities, "repeat_n_sum_k", probabilities_repeat_n_sum_k, -1 );
760
+ rb_define_singleton_method( Probabilities, "for_fair_die", probabilities_for_fair_die, 1 );
761
+ rb_define_singleton_method( Probabilities, "add_distributions", probabilities_add_distributions, 2 );
762
+ rb_define_singleton_method( Probabilities, "add_distributions_mult", probabilities_add_distributions_mult, 4 );
763
+ rb_define_singleton_method( Probabilities, "from_h", probabilities_from_h, 1 );
764
+ return;
765
+ }