dicey 0.15.1 → 0.15.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 315d35b504558fad6ce08ebd3e648903e9f85e97dd5388c20afbcff896c40825
4
- data.tar.gz: f4138742272906c291d44533391e3b63216c45f8bd99337f8c570e252bd0ce3b
3
+ metadata.gz: c18dbf421c9c19d438194ec9f63a9a22bfd18abb50d1f3b491a6e8067e3a04ae
4
+ data.tar.gz: e01eabaa58523f5fb2e1610ec671fc41a31f4aee7a583b64aa52d5bb9f083462
5
5
  SHA512:
6
- metadata.gz: 201115ad574e086ab99bf62d0129ae2dd3457cf2c06639d2e18f95c16699fa42f0ec72852aa13bdea408001a27fc366c1150d7cef2874f227957f5cd0e80bc40
7
- data.tar.gz: abfec7efee1b05e607432650a6e5de444e2cf75d9c462e9b6a0211cb48888234ccf4e95ddb50d431017ddc01d5969d6ca4b6a130faefd31b7f852dc3c41a9200
6
+ metadata.gz: 48759bc7919e8dbf699777a92735c586a8365f517c2c12d48cb725f12ada1fcfd6904e2c08e54ec7159267ad79003e7a1725a1e6e97694d7d4cbfb32198b2566
7
+ data.tar.gz: fa0cd4f20b6262cf927eb98cb5c296db2473910a27e2500323f80aeb5d2ac3224de143f67e0990fe555890535ff395d8514996934759984316d9fe22c4fb0e17
@@ -8,7 +8,7 @@ module Dicey
8
8
  #
9
9
  # These are well-known properties such as:
10
10
  # - min, max, mid-range;
11
- # - mode, median, arithmetic mean;
11
+ # - mode(s), median, arithmetic mean;
12
12
  # - important moments (expected value, variance, skewness, kurtosis).
13
13
  #
14
14
  # It is notable that most dice create symmetric distributions,
@@ -21,11 +21,13 @@ module Dicey
21
21
  # Calculate properties for a given distribution.
22
22
  #
23
23
  # Depending on values in the distribution, some properties may be undefined.
24
- # In such cases, only mode is guaranteed to be present.
24
+ # In such cases, only mode(s) are guaranteed to be present.
25
25
  #
26
- # @param distribution [Hash{Numeric => Numeric}
27
- # numeric distribution with pre-sorted keys
28
- # @return [Hash{Symbol => Numeric, Array<Numeric>}]
26
+ # On empty distribution, returns an empty hash.
27
+ #
28
+ # @param distribution [Hash{Numeric => Numeric}, Hash{Any => Numeric}]
29
+ # distribution with pre-sorted keys
30
+ # @return [Hash{Symbol => Numeric, Array<Numeric>, Array<Array<Numeric>>}]
29
31
  def call(distribution)
30
32
  return {} if distribution.empty?
31
33
 
@@ -40,6 +42,7 @@ module Dicey
40
42
 
41
43
  {
42
44
  mode: mode(outcomes, weights),
45
+ modes: modes(distribution),
43
46
  **range_characteristics(outcomes),
44
47
  **median(outcomes),
45
48
  **means(outcomes, weights),
@@ -52,13 +55,35 @@ module Dicey
52
55
  outcomes.select.with_index { |_, index| weights[index] == max_weight }
53
56
  end
54
57
 
58
+ def modes(distribution)
59
+ # Split into chunks with different weights,
60
+ # then select those with higher weights than their neighbors.
61
+ chunks = distribution.chunk_while { |(_, w_1), (_, w_2)| w_1 == w_2 }.to_a
62
+ return [chunks.first.map(&:first)] if chunks.size == 1
63
+
64
+ modes = []
65
+ add_local_mode(modes, nil, chunks[0], chunks[1])
66
+ chunks.each_cons(3).each do |chunk_before, chunk, chunk_after|
67
+ add_local_mode(modes, chunk_before, chunk, chunk_after)
68
+ end
69
+ add_local_mode(modes, chunks[-2], chunks[-1], nil)
70
+ modes
71
+ end
72
+
73
+ def add_local_mode(modes, chunk_before, chunk, chunk_after)
74
+ if (!chunk_before || chunk_before.first.last < chunk.first.last) &&
75
+ (!chunk_after || chunk_after.first.last < chunk.first.last)
76
+ modes << chunk.map(&:first)
77
+ end
78
+ end
79
+
55
80
  def range_characteristics(outcomes)
56
81
  min = outcomes.min
57
82
  max = outcomes.max
58
83
  {
59
84
  min: min,
60
85
  max: max,
61
- total_range: max - min,
86
+ range_length: max - min,
62
87
  mid_range: rational_to_integer(Rational(min + max, 2)),
63
88
  }
64
89
  rescue ArgumentError, TypeError, NoMethodError
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dicey
4
+ # @api private
4
5
  # Mix-in for converting rationals with denominator of 1 to integers.
5
6
  module RationalToInteger
7
+ private
8
+
6
9
  # Convert +value+ to +Integer+ if it's a +Rational+ with denominator of 1.
7
10
  # Otherwise, return +value+ as-is.
8
11
  #
data/lib/dicey/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dicey
4
- VERSION = "0.15.1"
4
+ VERSION = "0.15.2"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dicey
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.1
4
+ version: 0.15.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandr Bulancov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-10-07 00:00:00.000000000 Z
11
+ date: 2025-10-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Dicey provides a CLI executable and a Ruby API for fast calculation of
@@ -60,9 +60,9 @@ licenses:
60
60
  metadata:
61
61
  homepage_uri: https://github.com/trinistr/dicey
62
62
  bug_tracker_uri: https://github.com/trinistr/dicey/issues
63
- documentation_uri: https://rubydoc.info/gems/dicey/0.15.1
64
- source_code_uri: https://github.com/trinistr/dicey/tree/v0.15.1
65
- changelog_uri: https://github.com/trinistr/dicey/blob/v0.15.1/CHANGELOG.md
63
+ documentation_uri: https://rubydoc.info/gems/dicey/0.15.2
64
+ source_code_uri: https://github.com/trinistr/dicey/tree/v0.15.2
65
+ changelog_uri: https://github.com/trinistr/dicey/blob/v0.15.2/CHANGELOG.md
66
66
  rubygems_mfa_required: 'true'
67
67
  post_install_message:
68
68
  rdoc_options: