games_dice 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +11 -1
- data/.gitignore +0 -1
- data/.rubocop.yml +13 -48
- data/CHANGELOG.md +19 -0
- data/Gemfile +5 -3
- data/README.md +46 -5
- data/Rakefile +12 -5
- data/ext/games_dice/extconf.rb +0 -3
- data/ext/games_dice/probabilities.c +293 -193
- data/ext/games_dice/probabilities.h +2 -12
- data/games_dice.gemspec +1 -1
- data/lib/games_dice/bunch.rb +50 -36
- data/lib/games_dice/bunch_helpers.rb +35 -14
- data/lib/games_dice/complex_die.rb +64 -22
- data/lib/games_dice/complex_die_helpers.rb +122 -144
- data/lib/games_dice/dice.rb +61 -17
- data/lib/games_dice/dice_helpers.rb +37 -0
- data/lib/games_dice/die_result.rb +6 -8
- data/lib/games_dice/marshal.rb +2 -7
- data/lib/games_dice/parser.rb +30 -0
- data/lib/games_dice/probability_budget.rb +90 -0
- data/lib/games_dice/probability_calculation.rb +95 -0
- data/lib/games_dice/probability_shortcuts.rb +65 -0
- data/lib/games_dice/recipe_cache.rb +155 -0
- data/lib/games_dice/version.rb +1 -1
- data/lib/games_dice.rb +4 -0
- data/script/check_documentation.rb +10 -0
- data/spec/games_dice/bunch_regressions_spec.rb +46 -0
- data/spec/{bunch_spec.rb → games_dice/bunch_spec.rb} +65 -112
- data/spec/games_dice/complex_die_budget_spec.rb +60 -0
- data/spec/games_dice/complex_die_limits_spec.rb +119 -0
- data/spec/games_dice/complex_die_spec.rb +228 -0
- data/spec/games_dice/dice_mutation_spec.rb +165 -0
- data/spec/games_dice/dice_regressions_spec.rb +50 -0
- data/spec/games_dice/dice_spec.rb +61 -0
- data/spec/games_dice/die_result_spec.rb +221 -0
- data/spec/{die_spec.rb → games_dice/die_spec.rb} +15 -14
- data/spec/{map_rule_spec.rb → games_dice/map_rule_spec.rb} +8 -1
- data/spec/{parser_spec.rb → games_dice/parser_spec.rb} +12 -2
- data/spec/games_dice/probabilities_boundaries_spec.rb +161 -0
- data/spec/games_dice/probabilities_marshal_spec.rb +62 -0
- data/spec/games_dice/probabilities_spec.rb +484 -0
- data/spec/games_dice/probability_budget_spec.rb +17 -0
- data/spec/{reroll_rule_spec.rb → games_dice/reroll_rule_spec.rb} +5 -1
- data/spec/games_dice_spec.rb +382 -0
- data/spec/helpers.rb +64 -2
- data/spec/simple_cov_spec.rb +42 -0
- data/spec/support/coverage.rb +12 -0
- data/spec/support/validation.rb +14 -0
- data/spec/yard/cli/stats_spec.rb +43 -0
- metadata +29 -12
- data/spec/complex_die_spec.rb +0 -314
- data/spec/dice_spec.rb +0 -33
- data/spec/die_result_spec.rb +0 -254
- data/spec/fixtures/probs_fair_die_6.dat +0 -2
- data/spec/probability_spec.rb +0 -584
- data/spec/readme_spec.rb +0 -410
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// ext/games_dice/probabilities.c
|
|
2
2
|
|
|
3
3
|
#include "probabilities.h"
|
|
4
|
+
#include <limits.h>
|
|
5
|
+
#include <stdint.h>
|
|
6
|
+
#include <math.h>
|
|
4
7
|
|
|
5
8
|
// Ruby 1.8.7 compatibility patch
|
|
6
9
|
#ifndef DBL2NUM
|
|
@@ -21,22 +24,68 @@ VALUE Probabilities = Qnil;
|
|
|
21
24
|
// General utils
|
|
22
25
|
//
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
/*
|
|
28
|
+
* Owns the temporary distributions created during one native calculation.
|
|
29
|
+
*
|
|
30
|
+
* A local C pointer does not automatically free its allocation when the
|
|
31
|
+
* function returns. Ruby exceptions can also leave a calculation before
|
|
32
|
+
* ordinary cleanup code runs, including when a later allocation fails.
|
|
33
|
+
*
|
|
34
|
+
* Run the calculation through rb_ensure with cleanup_operation as its
|
|
35
|
+
* cleanup callback. create_probability_list adds each new distribution
|
|
36
|
+
* to owned before allocating its buffers. Its fields start at zero, so
|
|
37
|
+
* cleanup can safely free a partially constructed distribution.
|
|
38
|
+
*
|
|
39
|
+
* owned is the head of a linked list. Each ProbabilityList stores next
|
|
40
|
+
* and owner_link, which points to the pointer that currently refers to
|
|
41
|
+
* that entry. This lets destroy_probability_list unlink an entry when
|
|
42
|
+
* it is freed early, preventing cleanup from freeing it again.
|
|
43
|
+
*
|
|
44
|
+
* When returning a distribution to Ruby, perform_operation wraps it in
|
|
45
|
+
* a Ruby object before detaching it from owned. Ruby then handles its
|
|
46
|
+
* lifetime. If wrapping fails, the operation still owns the distribution
|
|
47
|
+
* and cleanup frees it.
|
|
48
|
+
*
|
|
49
|
+
* Iteration uses the same owner for its snapshot, keeping that storage
|
|
50
|
+
* alive even if a Ruby callback replaces the original distribution.
|
|
51
|
+
* Cleanup frees the snapshot after iteration, including exceptions and
|
|
52
|
+
* early block exits.
|
|
53
|
+
*/
|
|
54
|
+
typedef struct {
|
|
55
|
+
ProbabilityList *owned; /* Temporary distributions requiring cleanup. */
|
|
56
|
+
ProbabilityList *a, *b; /* Borrowed inputs, not owned by these pointers. */
|
|
57
|
+
int x, y, z; /* Arguments such as counts, weights and thresholds. */
|
|
58
|
+
int operation; /* Selects the calculation in perform_operation. */
|
|
59
|
+
} ProbabilityOperation;
|
|
60
|
+
|
|
61
|
+
static int checked_result( int64_t value ) {
|
|
62
|
+
if ( value < INT_MIN || value > INT_MAX ) {
|
|
63
|
+
rb_raise( rb_eRangeError, "Probability result is outside native integer range" );
|
|
29
64
|
}
|
|
30
|
-
return
|
|
65
|
+
return (int)value;
|
|
31
66
|
}
|
|
32
67
|
|
|
33
|
-
static int
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
68
|
+
static int checked_slots( int64_t slots ) {
|
|
69
|
+
if ( slots < 1 || slots > 1000000 ) {
|
|
70
|
+
rb_raise( rb_eArgError, "Bad number of probability slots" );
|
|
71
|
+
}
|
|
72
|
+
return (int)slots;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static int weighted_result( int a, int x, int b, int y ) {
|
|
76
|
+
int64_t ax = (int64_t)a * x, by = (int64_t)b * y;
|
|
77
|
+
if ( (by > 0 && ax > INT64_MAX - by) || (by < 0 && ax < INT64_MIN - by) ) {
|
|
78
|
+
rb_raise( rb_eRangeError, "Probability result is outside native integer range" );
|
|
38
79
|
}
|
|
39
|
-
return
|
|
80
|
+
return checked_result( ax + by );
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static void detach_probability_list( ProbabilityList *pl ) {
|
|
84
|
+
if ( !pl->owner_link ) return;
|
|
85
|
+
*pl->owner_link = pl->next;
|
|
86
|
+
if ( pl->next ) pl->next->owner_link = pl->owner_link;
|
|
87
|
+
pl->next = NULL;
|
|
88
|
+
pl->owner_link = NULL;
|
|
40
89
|
}
|
|
41
90
|
|
|
42
91
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
@@ -110,20 +159,19 @@ double num_arrangements( int *args, int nargs ) {
|
|
|
110
159
|
// Probability List basics - create, delete, copy
|
|
111
160
|
//
|
|
112
161
|
|
|
113
|
-
ProbabilityList *create_probability_list(
|
|
114
|
-
ProbabilityList *pl;
|
|
115
|
-
pl
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
pl->cumulative = NULL;
|
|
121
|
-
pl->slots = 0;
|
|
122
|
-
pl->offset = 0;
|
|
162
|
+
ProbabilityList *create_probability_list( ProbabilityOperation *op ) {
|
|
163
|
+
ProbabilityList *pl = ALLOC( ProbabilityList );
|
|
164
|
+
memset( pl, 0, sizeof(ProbabilityList) );
|
|
165
|
+
pl->next = op->owned;
|
|
166
|
+
pl->owner_link = &op->owned;
|
|
167
|
+
if ( op->owned ) op->owned->owner_link = &pl->next;
|
|
168
|
+
op->owned = pl;
|
|
123
169
|
return pl;
|
|
124
170
|
}
|
|
125
171
|
|
|
126
172
|
void destroy_probability_list( ProbabilityList *pl ) {
|
|
173
|
+
if ( !pl ) return;
|
|
174
|
+
detach_probability_list( pl );
|
|
127
175
|
xfree( pl->cumulative );
|
|
128
176
|
xfree( pl->probs );
|
|
129
177
|
xfree( pl );
|
|
@@ -173,8 +221,8 @@ double *alloc_probs_iv( ProbabilityList *pl, int slots, double iv ) {
|
|
|
173
221
|
return pr;
|
|
174
222
|
}
|
|
175
223
|
|
|
176
|
-
ProbabilityList *copy_probability_list( ProbabilityList *orig ) {
|
|
177
|
-
ProbabilityList *pl = create_probability_list();
|
|
224
|
+
ProbabilityList *copy_probability_list( ProbabilityOperation *op, ProbabilityList *orig ) {
|
|
225
|
+
ProbabilityList *pl = create_probability_list( op );
|
|
178
226
|
double *pr;
|
|
179
227
|
pr = alloc_probs( pl, orig->slots );
|
|
180
228
|
pl->offset = orig->offset;
|
|
@@ -183,8 +231,8 @@ ProbabilityList *copy_probability_list( ProbabilityList *orig ) {
|
|
|
183
231
|
return pl;
|
|
184
232
|
}
|
|
185
233
|
|
|
186
|
-
static ProbabilityList *new_basic_pl( int nslots, double iv, int o ) {
|
|
187
|
-
ProbabilityList *pl = create_probability_list();
|
|
234
|
+
static ProbabilityList *new_basic_pl( ProbabilityOperation *op, int nslots, double iv, int o ) {
|
|
235
|
+
ProbabilityList *pl = create_probability_list( op );
|
|
188
236
|
alloc_probs_iv( pl, nslots, iv );
|
|
189
237
|
pl->offset = o;
|
|
190
238
|
return pl;
|
|
@@ -202,16 +250,17 @@ static int pl_min( ProbabilityList *pl ) {
|
|
|
202
250
|
}
|
|
203
251
|
|
|
204
252
|
static int pl_max( ProbabilityList *pl ) {
|
|
205
|
-
return pl->offset + pl->slots - 1;
|
|
253
|
+
return checked_result( (int64_t)pl->offset + pl->slots - 1 );
|
|
206
254
|
}
|
|
207
255
|
|
|
208
|
-
ProbabilityList *pl_add_distributions( ProbabilityList *pl_a, ProbabilityList *pl_b ) {
|
|
256
|
+
ProbabilityList *pl_add_distributions( ProbabilityOperation *op, ProbabilityList *pl_a, ProbabilityList *pl_b ) {
|
|
209
257
|
double *pr;
|
|
210
|
-
int s = pl_a->slots + pl_b->slots - 1;
|
|
211
|
-
int o = pl_a->offset + pl_b->offset;
|
|
258
|
+
int s = checked_slots( (int64_t)pl_a->slots + pl_b->slots - 1 );
|
|
259
|
+
int o = checked_result( (int64_t)pl_a->offset + pl_b->offset );
|
|
260
|
+
checked_result( (int64_t)o + s - 1 );
|
|
212
261
|
int i,j;
|
|
213
262
|
|
|
214
|
-
ProbabilityList *pl = create_probability_list();
|
|
263
|
+
ProbabilityList *pl = create_probability_list( op );
|
|
215
264
|
pl->offset = o;
|
|
216
265
|
pr = alloc_probs_iv( pl, s, 0.0 );
|
|
217
266
|
for ( i=0; i < pl_a->slots; i++ ) { for ( j=0; j < pl_b->slots; j++ ) {
|
|
@@ -221,32 +270,31 @@ ProbabilityList *pl_add_distributions( ProbabilityList *pl_a, ProbabilityList *p
|
|
|
221
270
|
return pl;
|
|
222
271
|
}
|
|
223
272
|
|
|
224
|
-
ProbabilityList *pl_add_distributions_mult( int mul_a, ProbabilityList *pl_a, int mul_b, ProbabilityList *pl_b ) {
|
|
273
|
+
ProbabilityList *pl_add_distributions_mult( ProbabilityOperation *op, int mul_a, ProbabilityList *pl_a, int mul_b, ProbabilityList *pl_b ) {
|
|
225
274
|
int pts[4] = {
|
|
226
|
-
mul_a
|
|
227
|
-
mul_a
|
|
228
|
-
mul_a
|
|
229
|
-
mul_a
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
int
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
} }
|
|
275
|
+
weighted_result( mul_a, pl_min(pl_a), mul_b, pl_min(pl_b) ),
|
|
276
|
+
weighted_result( mul_a, pl_max(pl_a), mul_b, pl_min(pl_b) ),
|
|
277
|
+
weighted_result( mul_a, pl_min(pl_a), mul_b, pl_max(pl_b) ),
|
|
278
|
+
weighted_result( mul_a, pl_max(pl_a), mul_b, pl_max(pl_b) ) };
|
|
279
|
+
int low = pts[0], high = pts[0];
|
|
280
|
+
for ( int i = 1; i < 4; i++ ) {
|
|
281
|
+
if ( pts[i] < low ) low = pts[i];
|
|
282
|
+
if ( pts[i] > high ) high = pts[i];
|
|
283
|
+
}
|
|
284
|
+
int slots = checked_slots( 1 + (int64_t)high - low );
|
|
285
|
+
ProbabilityList *pl = new_basic_pl( op, slots, 0.0, low );
|
|
286
|
+
for ( int i = 0; i < pl_a->slots; i++ ) {
|
|
287
|
+
for ( int j = 0; j < pl_b->slots; j++ ) {
|
|
288
|
+
int64_t value = (int64_t)mul_a * (i + pl_a->offset) + (int64_t)mul_b * (j + pl_b->offset);
|
|
289
|
+
pl->probs[value - low] += pl_a->probs[i] * pl_b->probs[j];
|
|
290
|
+
}
|
|
291
|
+
}
|
|
244
292
|
calc_cumulative( pl );
|
|
245
293
|
return pl;
|
|
246
294
|
}
|
|
247
295
|
|
|
248
296
|
static double pl_p_eql( ProbabilityList *pl, int target ) {
|
|
249
|
-
|
|
297
|
+
int64_t idx = (int64_t)target - pl->offset;
|
|
250
298
|
if ( idx < 0 || idx >= pl->slots ) {
|
|
251
299
|
return 0.0;
|
|
252
300
|
}
|
|
@@ -258,11 +306,11 @@ static double pl_p_gt( ProbabilityList *pl, int target ) {
|
|
|
258
306
|
}
|
|
259
307
|
|
|
260
308
|
static double pl_p_lt( ProbabilityList *pl, int target ) {
|
|
261
|
-
return pl_p_le( pl, target - 1 );
|
|
309
|
+
return target == INT_MIN ? 0.0 : pl_p_le( pl, target - 1 );
|
|
262
310
|
}
|
|
263
311
|
|
|
264
312
|
static double pl_p_le( ProbabilityList *pl, int target ) {
|
|
265
|
-
|
|
313
|
+
int64_t idx = (int64_t)target - pl->offset;
|
|
266
314
|
if ( idx < 0 ) {
|
|
267
315
|
return 0.0;
|
|
268
316
|
}
|
|
@@ -273,7 +321,7 @@ static double pl_p_le( ProbabilityList *pl, int target ) {
|
|
|
273
321
|
}
|
|
274
322
|
|
|
275
323
|
static double pl_p_ge( ProbabilityList *pl, int target ) {
|
|
276
|
-
return 1.0 -
|
|
324
|
+
return 1.0 - pl_p_lt( pl, target );
|
|
277
325
|
}
|
|
278
326
|
|
|
279
327
|
static double pl_expected( ProbabilityList *pl ) {
|
|
@@ -288,7 +336,7 @@ static double pl_expected( ProbabilityList *pl ) {
|
|
|
288
336
|
return t;
|
|
289
337
|
}
|
|
290
338
|
|
|
291
|
-
ProbabilityList *pl_given_ge( ProbabilityList *pl, int target ) {
|
|
339
|
+
ProbabilityList *pl_given_ge( ProbabilityOperation *op, ProbabilityList *pl, int target ) {
|
|
292
340
|
int m = pl_min( pl );
|
|
293
341
|
double p, mult;
|
|
294
342
|
double *pr;
|
|
@@ -304,10 +352,10 @@ ProbabilityList *pl_given_ge( ProbabilityList *pl, int target ) {
|
|
|
304
352
|
rb_raise( rb_eRuntimeError, "Cannot calculate given probabilities, divide by zero" );
|
|
305
353
|
}
|
|
306
354
|
mult = 1.0/p;
|
|
307
|
-
s = pl->slots + pl->offset - target;
|
|
355
|
+
s = checked_slots( (int64_t)pl->slots + pl->offset - target );
|
|
308
356
|
pr = pl->probs;
|
|
309
357
|
|
|
310
|
-
new_pl = create_probability_list();
|
|
358
|
+
new_pl = create_probability_list( op );
|
|
311
359
|
new_pl->offset = target;
|
|
312
360
|
new_pr = alloc_probs( new_pl, s );
|
|
313
361
|
o = target - pl->offset;
|
|
@@ -319,7 +367,7 @@ ProbabilityList *pl_given_ge( ProbabilityList *pl, int target ) {
|
|
|
319
367
|
return new_pl;
|
|
320
368
|
}
|
|
321
369
|
|
|
322
|
-
ProbabilityList *pl_given_le( ProbabilityList *pl, int target ) {
|
|
370
|
+
ProbabilityList *pl_given_le( ProbabilityOperation *op, ProbabilityList *pl, int target ) {
|
|
323
371
|
int m = pl_max( pl );
|
|
324
372
|
double p, mult;
|
|
325
373
|
double *pr;
|
|
@@ -338,7 +386,7 @@ ProbabilityList *pl_given_le( ProbabilityList *pl, int target ) {
|
|
|
338
386
|
s = target - pl->offset + 1;
|
|
339
387
|
pr = pl->probs;
|
|
340
388
|
|
|
341
|
-
new_pl = create_probability_list();
|
|
389
|
+
new_pl = create_probability_list( op );
|
|
342
390
|
new_pl->offset = pl->offset;
|
|
343
391
|
new_pr = alloc_probs( new_pl, s );
|
|
344
392
|
|
|
@@ -349,34 +397,36 @@ ProbabilityList *pl_given_le( ProbabilityList *pl, int target ) {
|
|
|
349
397
|
return new_pl;
|
|
350
398
|
}
|
|
351
399
|
|
|
352
|
-
ProbabilityList *pl_repeat_sum( ProbabilityList *pl, int n ) {
|
|
400
|
+
ProbabilityList *pl_repeat_sum( ProbabilityOperation *op, ProbabilityList *pl, int n ) {
|
|
353
401
|
ProbabilityList *pd_power = NULL;
|
|
354
402
|
ProbabilityList *pd_result = NULL;
|
|
355
403
|
ProbabilityList *pd_next = NULL;
|
|
356
|
-
int
|
|
404
|
+
unsigned int remaining = (unsigned int)n;
|
|
357
405
|
|
|
358
406
|
if ( n < 1 ) {
|
|
359
407
|
rb_raise( rb_eRuntimeError, "Cannot calculate repeat_sum when n < 1" );
|
|
360
408
|
}
|
|
361
|
-
if ( n * pl->slots -
|
|
409
|
+
if ( (int64_t)n * (pl->slots - 1) >= 1000000 ) {
|
|
362
410
|
rb_raise( rb_eRuntimeError, "Too many probability slots" );
|
|
363
411
|
}
|
|
364
412
|
|
|
365
|
-
|
|
413
|
+
checked_result( (int64_t)n * pl_min(pl) );
|
|
414
|
+
checked_result( (int64_t)n * pl_max(pl) );
|
|
415
|
+
pd_power = copy_probability_list( op, pl );
|
|
366
416
|
|
|
367
417
|
while ( 1 ) {
|
|
368
|
-
if (
|
|
418
|
+
if ( remaining & 1 ) {
|
|
369
419
|
if ( pd_result ) {
|
|
370
|
-
pd_next = pl_add_distributions( pd_result, pd_power );
|
|
420
|
+
pd_next = pl_add_distributions( op, pd_result, pd_power );
|
|
371
421
|
destroy_probability_list( pd_result );
|
|
372
422
|
pd_result = pd_next;
|
|
373
423
|
} else {
|
|
374
|
-
pd_result = copy_probability_list( pd_power );
|
|
424
|
+
pd_result = copy_probability_list( op, pd_power );
|
|
375
425
|
}
|
|
376
426
|
}
|
|
377
|
-
|
|
378
|
-
if (
|
|
379
|
-
pd_next = pl_add_distributions( pd_power, pd_power );
|
|
427
|
+
remaining >>= 1;
|
|
428
|
+
if ( remaining == 0 ) break;
|
|
429
|
+
pd_next = pl_add_distributions( op, pd_power, pd_power );
|
|
380
430
|
destroy_probability_list( pd_power );
|
|
381
431
|
pd_power = pd_next;
|
|
382
432
|
}
|
|
@@ -400,28 +450,30 @@ void calc_p_table( ProbabilityList *pl, int q, int kbest, double *buffer ) {
|
|
|
400
450
|
}
|
|
401
451
|
|
|
402
452
|
// Assigns a list of pl variants to a buffer
|
|
403
|
-
void calc_keep_distributions( ProbabilityList *pl, int k, int q, int kbest, ProbabilityList **pl_array ) {
|
|
453
|
+
void calc_keep_distributions( ProbabilityOperation *op, ProbabilityList *pl, int k, int q, int kbest, ProbabilityList **pl_array ) {
|
|
404
454
|
ProbabilityList *pl_kd;
|
|
405
455
|
|
|
406
456
|
int n;
|
|
407
457
|
for ( n=0; n<k; n++) { pl_array[n] = NULL; }
|
|
408
|
-
pl_array[0] = new_basic_pl( 1, 1.0, q * k );
|
|
458
|
+
pl_array[0] = new_basic_pl( op, 1, 1.0, checked_result( (int64_t)q * k ) );
|
|
409
459
|
|
|
410
460
|
if ( kbest ) {
|
|
411
461
|
if ( pl_p_gt( pl, q ) > 0.0 && k > 1 ) {
|
|
412
|
-
pl_kd = pl_given_ge( pl, q + 1 );
|
|
462
|
+
pl_kd = pl_given_ge( op, pl, q + 1 );
|
|
413
463
|
for ( n = 1; n < k; n++ ) {
|
|
414
|
-
pl_array[n] = pl_repeat_sum( pl_kd, n );
|
|
415
|
-
(pl_array[n])->offset
|
|
464
|
+
pl_array[n] = pl_repeat_sum( op, pl_kd, n );
|
|
465
|
+
(pl_array[n])->offset = checked_result( (int64_t)(pl_array[n])->offset + (int64_t)q * (k - n) );
|
|
416
466
|
}
|
|
467
|
+
destroy_probability_list( pl_kd );
|
|
417
468
|
}
|
|
418
469
|
} else {
|
|
419
470
|
if ( pl_p_lt( pl, q ) > 0.0 && k > 1 ) {
|
|
420
|
-
pl_kd = pl_given_le( pl, q - 1 );
|
|
471
|
+
pl_kd = pl_given_le( op, pl, q - 1 );
|
|
421
472
|
for ( n = 1; n < k; n++ ) {
|
|
422
|
-
pl_array[n] = pl_repeat_sum( pl_kd, n );
|
|
423
|
-
(pl_array[n])->offset
|
|
473
|
+
pl_array[n] = pl_repeat_sum( op, pl_kd, n );
|
|
474
|
+
(pl_array[n])->offset = checked_result( (int64_t)(pl_array[n])->offset + (int64_t)q * (k - n) );
|
|
424
475
|
}
|
|
476
|
+
destroy_probability_list( pl_kd );
|
|
425
477
|
}
|
|
426
478
|
}
|
|
427
479
|
|
|
@@ -438,17 +490,17 @@ static void clear_pl_array( int k, ProbabilityList **pl_array ) {
|
|
|
438
490
|
return;
|
|
439
491
|
}
|
|
440
492
|
|
|
441
|
-
ProbabilityList *pl_repeat_n_sum_k( ProbabilityList *pl, int n, int k, int kbest ) {
|
|
493
|
+
ProbabilityList *pl_repeat_n_sum_k( ProbabilityOperation *op, ProbabilityList *pl, int n, int k, int kbest ) {
|
|
442
494
|
// Table of probabilities ( reject, maybe, keep ) for each "pivot point"
|
|
443
495
|
double p_table[3];
|
|
444
496
|
int keep_combos[3];
|
|
445
497
|
// Table of distributions for each count of > pivot point (vs == pivot point)
|
|
446
|
-
ProbabilityList *keep_distributions[171];
|
|
498
|
+
ProbabilityList *keep_distributions[171] = { NULL };
|
|
447
499
|
ProbabilityList *kd;
|
|
448
500
|
ProbabilityList *pl_result = NULL;
|
|
449
501
|
|
|
450
502
|
double *pr;
|
|
451
|
-
int d
|
|
503
|
+
int d;
|
|
452
504
|
int i, j, q, dn, kn, mn, kdq;
|
|
453
505
|
double p_sequence;
|
|
454
506
|
|
|
@@ -459,17 +511,21 @@ ProbabilityList *pl_repeat_n_sum_k( ProbabilityList *pl, int n, int k, int kbest
|
|
|
459
511
|
rb_raise( rb_eRuntimeError, "Cannot calculate repeat_sum_k when k < 1" );
|
|
460
512
|
}
|
|
461
513
|
if ( k >= n ) {
|
|
462
|
-
return pl_repeat_sum( pl, n );
|
|
514
|
+
return pl_repeat_sum( op, pl, n );
|
|
463
515
|
}
|
|
464
|
-
if ( k * pl->slots -
|
|
516
|
+
if ( (int64_t)k * (pl->slots - 1) >= 1000000 ) {
|
|
465
517
|
rb_raise( rb_eRuntimeError, "Too many probability slots" );
|
|
466
518
|
}
|
|
467
519
|
if ( n > 170 ) {
|
|
468
520
|
rb_raise( rb_eRuntimeError, "Too many dice to calculate combinations" );
|
|
469
521
|
}
|
|
470
522
|
|
|
523
|
+
d = n - k;
|
|
524
|
+
checked_result( (int64_t)k * pl_min(pl) );
|
|
525
|
+
checked_result( (int64_t)k * pl_max(pl) );
|
|
526
|
+
|
|
471
527
|
// Init target
|
|
472
|
-
pl_result = create_probability_list();
|
|
528
|
+
pl_result = create_probability_list( op );
|
|
473
529
|
pr = alloc_probs_iv( pl_result, 1 + k * (pl->slots - 1), 0.0 );
|
|
474
530
|
pl_result->offset = pl->offset * k;
|
|
475
531
|
|
|
@@ -477,7 +533,7 @@ ProbabilityList *pl_repeat_n_sum_k( ProbabilityList *pl, int n, int k, int kbest
|
|
|
477
533
|
if ( pl->probs[i] <= 0.0 ) continue;
|
|
478
534
|
|
|
479
535
|
q = i + pl->offset;
|
|
480
|
-
calc_keep_distributions( pl, k, q, kbest, keep_distributions );
|
|
536
|
+
calc_keep_distributions( op, pl, k, q, kbest, keep_distributions );
|
|
481
537
|
calc_p_table( pl, q, kbest, p_table );
|
|
482
538
|
|
|
483
539
|
for ( kn = 0; kn < k; kn++ ) {
|
|
@@ -550,7 +606,9 @@ static inline VALUE pl_as_ruby_class( ProbabilityList *pl, VALUE klass ) {
|
|
|
550
606
|
}
|
|
551
607
|
|
|
552
608
|
VALUE pl_alloc(VALUE klass) {
|
|
553
|
-
|
|
609
|
+
ProbabilityList *pl;
|
|
610
|
+
VALUE obj = TypedData_Make_Struct( klass, ProbabilityList, &probability_list_type, pl );
|
|
611
|
+
return obj;
|
|
554
612
|
}
|
|
555
613
|
|
|
556
614
|
static inline ProbabilityList *get_probability_list( VALUE obj ) {
|
|
@@ -565,36 +623,79 @@ void assert_value_wraps_pl( VALUE obj ) {
|
|
|
565
623
|
}
|
|
566
624
|
}
|
|
567
625
|
|
|
568
|
-
|
|
569
|
-
int validate_key_value( VALUE key, VALUE val, VALUE obj ) {
|
|
570
|
-
int k = NUM2INT( key );
|
|
571
|
-
ProbabilityList *pl = get_probability_list( obj );
|
|
572
|
-
// Not assigned (to avoid "unused" warning), but this throws execption if val cannot be coerced to double
|
|
573
|
-
NUM2DBL( val );
|
|
626
|
+
enum { OP_ADD, OP_WEIGHTED, OP_GE, OP_LE, OP_REPEAT, OP_KEEP, OP_COPY };
|
|
574
627
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
628
|
+
static VALUE cleanup_operation( VALUE data ) {
|
|
629
|
+
ProbabilityOperation *op = (ProbabilityOperation *)data;
|
|
630
|
+
while ( op->owned ) destroy_probability_list( op->owned );
|
|
631
|
+
return Qnil;
|
|
632
|
+
}
|
|
578
633
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
634
|
+
static VALUE perform_operation( VALUE data ) {
|
|
635
|
+
ProbabilityOperation *op = (ProbabilityOperation *)data;
|
|
636
|
+
ProbabilityList *result;
|
|
637
|
+
switch ( op->operation ) {
|
|
638
|
+
case OP_ADD: result = pl_add_distributions( op, op->a, op->b ); break;
|
|
639
|
+
case OP_WEIGHTED: result = pl_add_distributions_mult( op, op->x, op->a, op->y, op->b ); break;
|
|
640
|
+
case OP_GE: result = pl_given_ge( op, op->a, op->x ); break;
|
|
641
|
+
case OP_LE: result = pl_given_le( op, op->a, op->x ); break;
|
|
642
|
+
case OP_REPEAT: result = pl_repeat_sum( op, op->a, op->x ); break;
|
|
643
|
+
case OP_KEEP: result = pl_repeat_n_sum_k( op, op->a, op->x, op->y, op->z ); break;
|
|
644
|
+
default: result = copy_probability_list( op, op->a ); break;
|
|
588
645
|
}
|
|
646
|
+
VALUE obj = pl_as_ruby_class( result, Probabilities );
|
|
647
|
+
detach_probability_list( result );
|
|
648
|
+
return obj;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
static VALUE operate( int operation, ProbabilityList *a, ProbabilityList *b, int x, int y, int z ) {
|
|
652
|
+
ProbabilityOperation op = { .owned = NULL, .a = a, .b = b, .x = x, .y = y, .z = z, .operation = operation };
|
|
653
|
+
return rb_ensure( perform_operation, (VALUE)&op, cleanup_operation, (VALUE)&op );
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/* A staging Ruby object owns buffers across coercion/allocation exceptions. */
|
|
657
|
+
static VALUE replace_distribution( VALUE receiver, VALUE staged ) {
|
|
658
|
+
ProbabilityList *old = get_probability_list( receiver );
|
|
659
|
+
RTYPEDDATA_DATA(receiver) = get_probability_list( staged );
|
|
660
|
+
RTYPEDDATA_DATA(staged) = NULL;
|
|
661
|
+
destroy_probability_list( old );
|
|
662
|
+
RB_GC_GUARD(staged);
|
|
663
|
+
return receiver;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
static double probability_value( VALUE value ) {
|
|
667
|
+
double p = NUM2DBL(value);
|
|
668
|
+
if ( p < 0.0 ) rb_raise( rb_eArgError, "Negative probability not allowed" );
|
|
669
|
+
if ( p > 1.0 ) rb_raise( rb_eArgError, "Probability must be in range 0.0..1.0" );
|
|
670
|
+
if ( !isfinite(p) ) rb_raise( rb_eArgError, "Probability must be finite" );
|
|
671
|
+
return p;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
static void validate_mass( ProbabilityList *pl ) {
|
|
675
|
+
double error = calc_cumulative( pl ) - 1.0;
|
|
676
|
+
if ( error < -1.0e-8 ) rb_raise( rb_eArgError, "Total probabilities are less than 1.0" );
|
|
677
|
+
if ( error > 1.0e-8 ) rb_raise( rb_eArgError, "Total probabilities are greater than 1.0" );
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
static int normalize_pair( VALUE key, VALUE value, VALUE normalized ) {
|
|
681
|
+
int k = NUM2INT(key);
|
|
682
|
+
double p = probability_value(value);
|
|
683
|
+
rb_hash_aset( normalized, INT2NUM(k), DBL2NUM(p) );
|
|
589
684
|
return ST_CONTINUE;
|
|
590
685
|
}
|
|
591
686
|
|
|
592
|
-
|
|
593
|
-
int
|
|
594
|
-
int k = NUM2INT(
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
687
|
+
static int hash_bounds( VALUE key, VALUE value, VALUE data ) {
|
|
688
|
+
int *bounds = (int *)data;
|
|
689
|
+
int k = NUM2INT(key);
|
|
690
|
+
(void)value;
|
|
691
|
+
if ( k < bounds[0] ) bounds[0] = k;
|
|
692
|
+
if ( k > bounds[1] ) bounds[1] = k;
|
|
693
|
+
return ST_CONTINUE;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
static int copy_pair( VALUE key, VALUE value, VALUE data ) {
|
|
697
|
+
ProbabilityList *pl = (ProbabilityList *)data;
|
|
698
|
+
pl->probs[NUM2INT(key) - pl->offset] = NUM2DBL(value);
|
|
598
699
|
return ST_CONTINUE;
|
|
599
700
|
}
|
|
600
701
|
|
|
@@ -611,33 +712,24 @@ int copy_key_value( VALUE key, VALUE val, VALUE obj ) {
|
|
|
611
712
|
* @return [GamesDice::Probabilities]
|
|
612
713
|
*/
|
|
613
714
|
VALUE probabilities_initialize( VALUE self, VALUE arr, VALUE offset ) {
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
ProbabilityList *pl;
|
|
617
|
-
double *pr;
|
|
618
|
-
|
|
619
|
-
o = NUM2INT(offset);
|
|
715
|
+
rb_check_frozen(self);
|
|
716
|
+
int o = NUM2INT(offset);
|
|
620
717
|
Check_Type( arr, T_ARRAY );
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
p_item = NUM2DBL( rb_ary_entry( arr, i ) );
|
|
627
|
-
if ( p_item < 0.0 ) {
|
|
628
|
-
rb_raise( rb_eArgError, "Negative probability not allowed" );
|
|
629
|
-
} else if ( p_item > 1.0 ) {
|
|
630
|
-
rb_raise( rb_eArgError, "Probability must be in range 0.0..1.0" );
|
|
631
|
-
}
|
|
632
|
-
pr[i] = p_item;
|
|
633
|
-
}
|
|
634
|
-
error = calc_cumulative( pl ) - 1.0;
|
|
635
|
-
if ( error < -1.0e-8 ) {
|
|
636
|
-
rb_raise( rb_eArgError, "Total probabilities are less than 1.0" );
|
|
637
|
-
} else if ( error > 1.0e-8 ) {
|
|
638
|
-
rb_raise( rb_eArgError, "Total probabilities are greater than 1.0" );
|
|
718
|
+
int slots = checked_slots( RARRAY_LEN(arr) );
|
|
719
|
+
checked_result( (int64_t)o + slots - 1 );
|
|
720
|
+
VALUE normalized = rb_ary_dup(arr);
|
|
721
|
+
for ( int i = 0; i < slots; i++ ) {
|
|
722
|
+
rb_ary_store( normalized, i, DBL2NUM(probability_value(rb_ary_entry(normalized, i))) );
|
|
639
723
|
}
|
|
640
|
-
|
|
724
|
+
VALUE staged = pl_alloc( Probabilities );
|
|
725
|
+
ProbabilityList *pl = get_probability_list( staged );
|
|
726
|
+
pl->offset = o;
|
|
727
|
+
alloc_probs( pl, slots );
|
|
728
|
+
for ( int i = 0; i < slots; i++ ) pl->probs[i] = NUM2DBL(rb_ary_entry(normalized, i));
|
|
729
|
+
validate_mass( pl );
|
|
730
|
+
RB_GC_GUARD(normalized);
|
|
731
|
+
rb_check_frozen(self);
|
|
732
|
+
return replace_distribution( self, staged );
|
|
641
733
|
}
|
|
642
734
|
|
|
643
735
|
/*
|
|
@@ -646,20 +738,11 @@ VALUE probabilities_initialize( VALUE self, VALUE arr, VALUE offset ) {
|
|
|
646
738
|
* @return [GamesDice::Probabilities]
|
|
647
739
|
*/
|
|
648
740
|
VALUE probabilities_initialize_copy( VALUE copy, VALUE orig ) {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
pl_copy = get_probability_list( copy );
|
|
655
|
-
pl_orig = get_probability_list( orig );
|
|
656
|
-
|
|
657
|
-
pr = alloc_probs( pl_copy, pl_orig->slots );
|
|
658
|
-
pl_copy->offset = pl_orig->offset;
|
|
659
|
-
memcpy( pr, pl_orig->probs, pl_orig->slots * sizeof(double) );
|
|
660
|
-
memcpy( pl_copy->cumulative, pl_orig->cumulative, pl_orig->slots * sizeof(double) );;
|
|
661
|
-
|
|
662
|
-
return copy;
|
|
741
|
+
if ( copy == orig ) return copy;
|
|
742
|
+
rb_check_frozen(copy);
|
|
743
|
+
VALUE staged = operate( OP_COPY, get_probability_list(orig), NULL, 0, 0, 0 );
|
|
744
|
+
RB_GC_GUARD(orig);
|
|
745
|
+
return replace_distribution( copy, staged );
|
|
663
746
|
}
|
|
664
747
|
|
|
665
748
|
/*
|
|
@@ -709,7 +792,8 @@ VALUE probabilities_max( VALUE self ) {
|
|
|
709
792
|
* @return [Float] in range (0.0..1.0)
|
|
710
793
|
*/
|
|
711
794
|
VALUE probabilites_p_eql( VALUE self, VALUE target ) {
|
|
712
|
-
|
|
795
|
+
int t = NUM2INT(target);
|
|
796
|
+
return DBL2NUM( pl_p_eql( get_probability_list( self ), t ) );
|
|
713
797
|
}
|
|
714
798
|
|
|
715
799
|
/*
|
|
@@ -718,7 +802,8 @@ VALUE probabilites_p_eql( VALUE self, VALUE target ) {
|
|
|
718
802
|
* @return [Float] in range (0.0..1.0)
|
|
719
803
|
*/
|
|
720
804
|
VALUE probabilites_p_gt( VALUE self, VALUE target ) {
|
|
721
|
-
|
|
805
|
+
int t = NUM2INT(target);
|
|
806
|
+
return DBL2NUM( pl_p_gt( get_probability_list( self ), t ) );
|
|
722
807
|
}
|
|
723
808
|
|
|
724
809
|
/*
|
|
@@ -727,7 +812,8 @@ VALUE probabilites_p_gt( VALUE self, VALUE target ) {
|
|
|
727
812
|
* @return [Float] in range (0.0..1.0)
|
|
728
813
|
*/
|
|
729
814
|
VALUE probabilites_p_ge( VALUE self, VALUE target ) {
|
|
730
|
-
|
|
815
|
+
int t = NUM2INT(target);
|
|
816
|
+
return DBL2NUM( pl_p_ge( get_probability_list( self ), t ) );
|
|
731
817
|
}
|
|
732
818
|
|
|
733
819
|
/*
|
|
@@ -736,7 +822,8 @@ VALUE probabilites_p_ge( VALUE self, VALUE target ) {
|
|
|
736
822
|
* @return [Float] in range (0.0..1.0)
|
|
737
823
|
*/
|
|
738
824
|
VALUE probabilites_p_le( VALUE self, VALUE target ) {
|
|
739
|
-
|
|
825
|
+
int t = NUM2INT(target);
|
|
826
|
+
return DBL2NUM( pl_p_le( get_probability_list( self ), t ) );
|
|
740
827
|
}
|
|
741
828
|
|
|
742
829
|
/*
|
|
@@ -745,7 +832,8 @@ VALUE probabilites_p_le( VALUE self, VALUE target ) {
|
|
|
745
832
|
* @return [Float] in range (0.0..1.0)
|
|
746
833
|
*/
|
|
747
834
|
VALUE probabilites_p_lt( VALUE self, VALUE target ) {
|
|
748
|
-
|
|
835
|
+
int t = NUM2INT(target);
|
|
836
|
+
return DBL2NUM( pl_p_lt( get_probability_list( self ), t ) );
|
|
749
837
|
}
|
|
750
838
|
|
|
751
839
|
/*
|
|
@@ -767,7 +855,9 @@ VALUE probabilites_expected( VALUE self ) {
|
|
|
767
855
|
VALUE probabilities_given_ge( VALUE self, VALUE target ) {
|
|
768
856
|
int t = NUM2INT(target);
|
|
769
857
|
ProbabilityList *pl = get_probability_list( self );
|
|
770
|
-
|
|
858
|
+
VALUE result = operate( OP_GE, pl, NULL, t, 0, 0 );
|
|
859
|
+
RB_GC_GUARD(self);
|
|
860
|
+
return result;
|
|
771
861
|
}
|
|
772
862
|
|
|
773
863
|
/*
|
|
@@ -779,7 +869,9 @@ VALUE probabilities_given_ge( VALUE self, VALUE target ) {
|
|
|
779
869
|
VALUE probabilities_given_le( VALUE self, VALUE target ) {
|
|
780
870
|
int t = NUM2INT(target);
|
|
781
871
|
ProbabilityList *pl = get_probability_list( self );
|
|
782
|
-
|
|
872
|
+
VALUE result = operate( OP_LE, pl, NULL, t, 0, 0 );
|
|
873
|
+
RB_GC_GUARD(self);
|
|
874
|
+
return result;
|
|
783
875
|
}
|
|
784
876
|
|
|
785
877
|
/*
|
|
@@ -792,7 +884,9 @@ VALUE probabilities_given_le( VALUE self, VALUE target ) {
|
|
|
792
884
|
VALUE probabilities_repeat_sum( VALUE self, VALUE nsum ) {
|
|
793
885
|
int n = NUM2INT(nsum);
|
|
794
886
|
ProbabilityList *pl = get_probability_list( self );
|
|
795
|
-
|
|
887
|
+
VALUE result = operate( OP_REPEAT, pl, NULL, n, 0, 0 );
|
|
888
|
+
RB_GC_GUARD(self);
|
|
889
|
+
return result;
|
|
796
890
|
}
|
|
797
891
|
|
|
798
892
|
/*
|
|
@@ -822,17 +916,15 @@ VALUE probabilities_repeat_n_sum_k( int argc, VALUE* argv, VALUE self ) {
|
|
|
822
916
|
n = NUM2INT(nsum);
|
|
823
917
|
k = NUM2INT(nkeepers);
|
|
824
918
|
pl = get_probability_list( self );
|
|
825
|
-
|
|
919
|
+
VALUE result = operate( OP_KEEP, pl, NULL, n, k, keep_best );
|
|
920
|
+
RB_GC_GUARD(self);
|
|
921
|
+
return result;
|
|
826
922
|
}
|
|
827
923
|
|
|
828
|
-
|
|
829
|
-
*
|
|
830
|
-
|
|
831
|
-
*
|
|
832
|
-
* @return [GamesDice::Probabilities] this object
|
|
833
|
-
*/
|
|
834
|
-
VALUE probabilities_each( VALUE self ) {
|
|
835
|
-
ProbabilityList *pl = get_probability_list( self );
|
|
924
|
+
static VALUE yield_probability_pairs( VALUE data ) {
|
|
925
|
+
ProbabilityOperation *op = (ProbabilityOperation *)data;
|
|
926
|
+
/* Keep the snapshot native-owned until every callback has finished. */
|
|
927
|
+
ProbabilityList *pl = copy_probability_list( op, op->a );
|
|
836
928
|
int i;
|
|
837
929
|
double *pr = pl->probs;
|
|
838
930
|
int o = pl->offset;
|
|
@@ -844,6 +936,19 @@ VALUE probabilities_each( VALUE self ) {
|
|
|
844
936
|
rb_yield( a );
|
|
845
937
|
}
|
|
846
938
|
}
|
|
939
|
+
return Qnil;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/*
|
|
943
|
+
* Iterates through value, probability pairs
|
|
944
|
+
* @yieldparam [Integer] result A result that may be possible in the dice scheme
|
|
945
|
+
* @yieldparam [Float] probability Probability of result, in range 0.0..1.0
|
|
946
|
+
* @return [GamesDice::Probabilities] this object
|
|
947
|
+
*/
|
|
948
|
+
VALUE probabilities_each( VALUE self ) {
|
|
949
|
+
/* Ruby callbacks may replace self's storage through marshal_load/initialize. */
|
|
950
|
+
ProbabilityOperation op = { .owned = NULL, .a = get_probability_list(self) };
|
|
951
|
+
rb_ensure( yield_probability_pairs, (VALUE)&op, cleanup_operation, (VALUE)&op );
|
|
847
952
|
return self;
|
|
848
953
|
}
|
|
849
954
|
|
|
@@ -878,32 +983,21 @@ VALUE probabilities_for_fair_die( VALUE self, VALUE sides ) {
|
|
|
878
983
|
* @return [GamesDice::Probabilities]
|
|
879
984
|
*/
|
|
880
985
|
VALUE probabilities_from_h( VALUE self, VALUE hash ) {
|
|
881
|
-
VALUE obj;
|
|
882
|
-
ProbabilityList *pl;
|
|
883
|
-
double error;
|
|
884
|
-
|
|
885
986
|
Check_Type( hash, T_HASH );
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
error = calc_cumulative( pl ) - 1.0;
|
|
902
|
-
if ( error < -1.0e-8 ) {
|
|
903
|
-
rb_raise( rb_eArgError, "Total probabilities are less than 1.0" );
|
|
904
|
-
} else if ( error > 1.0e-8 ) {
|
|
905
|
-
rb_raise( rb_eArgError, "Total probabilities are greater than 1.0" );
|
|
906
|
-
}
|
|
987
|
+
VALUE normalized = rb_hash_new();
|
|
988
|
+
VALUE input = rb_hash_dup(hash);
|
|
989
|
+
rb_hash_foreach( input, normalize_pair, normalized );
|
|
990
|
+
RB_GC_GUARD(input);
|
|
991
|
+
int bounds[2] = { INT_MAX, INT_MIN };
|
|
992
|
+
rb_hash_foreach( normalized, hash_bounds, (VALUE)bounds );
|
|
993
|
+
int slots = checked_slots( 1 + (int64_t)bounds[1] - bounds[0] );
|
|
994
|
+
VALUE obj = pl_alloc( Probabilities );
|
|
995
|
+
ProbabilityList *pl = get_probability_list( obj );
|
|
996
|
+
pl->offset = bounds[0];
|
|
997
|
+
alloc_probs_iv( pl, slots, 0.0 );
|
|
998
|
+
rb_hash_foreach( normalized, copy_pair, (VALUE)pl );
|
|
999
|
+
validate_mass( pl );
|
|
1000
|
+
RB_GC_GUARD(normalized);
|
|
907
1001
|
return obj;
|
|
908
1002
|
}
|
|
909
1003
|
|
|
@@ -923,7 +1017,10 @@ VALUE probabilities_add_distributions( VALUE self, VALUE gdpa, VALUE gdpb ) {
|
|
|
923
1017
|
assert_value_wraps_pl( gdpb );
|
|
924
1018
|
pl_a = get_probability_list( gdpa );
|
|
925
1019
|
pl_b = get_probability_list( gdpb );
|
|
926
|
-
|
|
1020
|
+
VALUE result = operate( OP_ADD, pl_a, pl_b, 0, 0, 0 );
|
|
1021
|
+
RB_GC_GUARD(gdpa);
|
|
1022
|
+
RB_GC_GUARD(gdpb);
|
|
1023
|
+
return result;
|
|
927
1024
|
}
|
|
928
1025
|
|
|
929
1026
|
/*
|
|
@@ -944,10 +1041,13 @@ VALUE probabilities_add_distributions_mult( VALUE self, VALUE m_a, VALUE gdpa, V
|
|
|
944
1041
|
assert_value_wraps_pl( gdpa );
|
|
945
1042
|
assert_value_wraps_pl( gdpb );
|
|
946
1043
|
mul_a = NUM2INT( m_a );
|
|
947
|
-
pl_a = get_probability_list( gdpa );
|
|
948
1044
|
mul_b = NUM2INT( m_b );
|
|
1045
|
+
pl_a = get_probability_list( gdpa );
|
|
949
1046
|
pl_b = get_probability_list( gdpb );
|
|
950
|
-
|
|
1047
|
+
VALUE result = operate( OP_WEIGHTED, pl_a, pl_b, mul_a, mul_b, 0 );
|
|
1048
|
+
RB_GC_GUARD(gdpa);
|
|
1049
|
+
RB_GC_GUARD(gdpb);
|
|
1050
|
+
return result;
|
|
951
1051
|
}
|
|
952
1052
|
|
|
953
1053
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|