tiny_outcome 2.1.0 → 3.1.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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tiny_outcome.rb +58 -21
  3. metadata +2 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ccac5bae17f87452576130e604477b25fbaaa70785e0a41894077e8896521a28
4
- data.tar.gz: f108cdf6533898223d088a169645d2b0ddc897c857c725c713086f42519a9d74
3
+ metadata.gz: 7e887122fe969136656c57bf1e8a240df3500f0120694891be4f9ba26cf9c694
4
+ data.tar.gz: 92e30c4c3c72afc4942181ede0badc1865b297c0444a1c7ec7cce272e4a7dac8
5
5
  SHA512:
6
- metadata.gz: 12b256504992f76ca4e6a22af2b5a9e24699062765f3bf1acb6f9d1e24f9fceefcbf865e856371ab4bf85c9a20fef6923e8582bb9c87b37571bbdba4d1271752
7
- data.tar.gz: fedc09804f2e2c2007cf05e83f0971a2f54a5e9dcb18a2cf245fd5d907e60813f83de23e55024b323bd065cda7acc08d6504928ea1706871b465831326ac33fe
6
+ metadata.gz: 31bf45e93abd24309b440599f7006573dd6b2beb19a588234b6f15aaed4334bde062b252f8bdecf2a7f8b72fe3a42def7a55d38a225b8e37cb19af2dfc375977
7
+ data.tar.gz: b828c61ee05e08de9041bdb5b17a95005ef2cf315419bd3806dc9feec2efb5ba2138231bd8ff9d4bc74e5d02079848c4356d0dc779d02709a12623cccc6ce66d
data/lib/tiny_outcome.rb CHANGED
@@ -30,7 +30,12 @@ class TinyOutcome
30
30
  :samples,
31
31
  :warmth,
32
32
  :warmup,
33
- :value
33
+ :probability,
34
+ :one_count,
35
+ :value,
36
+ :min,
37
+ :max,
38
+ :avg
34
39
 
35
40
  WARM_FULL = :full
36
41
  WARM_TWO_THIRDS = :two_thirds
@@ -44,9 +49,15 @@ class TinyOutcome
44
49
  # samples that we can trust the probability output
45
50
  def initialize(precision, warmup=WARM_FULL)
46
51
  @precision = precision
52
+ @probability = 0.0
53
+ @one_count = 0
47
54
  @samples = 0
55
+ @min = -1.0
56
+ @max = -1.0
57
+ @avg = -1.0
48
58
  @warmth = 0
49
- @value = 0
59
+ @value = [0] * @precision
60
+ @value_index = 0
50
61
  @warmup = case warmup
51
62
  when WARM_FULL then precision
52
63
  when WARM_TWO_THIRDS then (precision / 3) * 2
@@ -59,35 +70,37 @@ class TinyOutcome
59
70
  end
60
71
  end
61
72
 
62
- # add a sample to the historic outcomes. the new sample is added to the
63
- # low-order bits. the new sample is literally left-shifted into the value. the
64
- # only reason this is a custom method is because some metadata needs to be
65
- # updated when a new sample is added
73
+ def numeric_value
74
+ (full? ? @value.rotate(@value_index) : @value)[..(samples-1)].join.to_i(2)
75
+ end
76
+
77
+ # add a sample to the historic outcomes
66
78
  def <<(sample)
67
79
  raise "Invalid sample: #{sample}" unless sample == 0 || sample == 1
68
80
 
69
- @value = ((value << 1) | sample) & (2**precision - 1)
81
+ removing_one = full? && @value[(@value_index + 1) % @precision] == 1
82
+
83
+ @value[@value_index] = sample
84
+ @value_index = (@value_index + 1) % @precision
70
85
  @warmth += 1 unless warmth == warmup
71
- @samples += 1 unless samples == precision
86
+ @samples += 1 unless full?
87
+
88
+ # percentage of 1s out of the existing samples
89
+ #
90
+ # number of 1s
91
+ # probabilty = ---------------
92
+ # total samples
93
+ @one_count -= 1 if removing_one
94
+ @one_count += 1 if sample == 1
95
+ @probability = @one_count / samples.to_f
72
96
 
73
- value
97
+ @value
74
98
  end
75
99
 
76
100
  # true if #probability is >= percentage
77
101
  # false otherwise
78
102
  def winner_at?(percentage)
79
- probability >= percentage
80
- end
81
-
82
- # float: 0.0-1.0
83
- # percentage of 1s out of the existing samples
84
- #
85
- # number of 1s
86
- # probabilty = ---------------
87
- # total samples
88
- def probability
89
- return 0.0 if samples == 0
90
- value.to_s(2).count('1') / samples.to_f
103
+ @probability >= percentage
91
104
  end
92
105
 
93
106
  # true if we've received at least warmup number of samples
@@ -107,6 +120,30 @@ class TinyOutcome
107
120
  samples == precision
108
121
  end
109
122
 
123
+ # updates, and memoizes, the min/max/avg numbers. if you read the min/max/avg
124
+ # attributes you are getting the MEMOIZED values.
125
+ def update_stats!
126
+ return if @samples == 0
127
+
128
+ @min = 1.0
129
+ @max = 0.0
130
+ @avg = 0.0
131
+
132
+ sum = 0.0
133
+ raw = (full? ? @value.rotate(@value_index) : @value)[..(samples-1)]
134
+ group_size = [@samples, 100].min
135
+ num_groups = @samples - group_size
136
+ raw.each_cons(group_size) do |samples_group|
137
+ curr = samples_group.count(1) / samples.to_f
138
+ sum += curr
139
+
140
+ @min = curr if curr < @min
141
+ @max = curr if curr > @max
142
+ end
143
+
144
+ @avg = sum / (@num_groups.to_f + 1.0)
145
+ end
146
+
110
147
  # convenient way to see what's up
111
148
  def to_hash
112
149
  [:value,
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_outcome
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Lunt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-03 00:00:00.000000000 Z
11
+ date: 2023-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: m
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: minitest
29
15
  requirement: !ruby/object:Gem::Requirement