monotonic_grouper 1.0.3 → 1.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.
- checksums.yaml +4 -4
- data/README.md +12 -11
- data/ext/monotonic_grouper/monotonic_grouper.c +144 -128
- data/lib/monotonic_grouper/monotonic_grouper.bundle +0 -0
- data/lib/monotonic_grouper/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a150a2735dc99df4715193f8075ad6a739723c170a25bc9ae61d628e50a8661f
|
|
4
|
+
data.tar.gz: eaa156e8d8f882969a5f9b07f54e4353c996a7adaf22181061e7ff80d0e7309e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 884a86506cff344b18254dbbaccb73ac5311ea98031995a6ada6e2b299e93c03170b0b2e3e513bfdd878a36769441fc043c390e41985011fa1db04cb86995f72
|
|
7
|
+
data.tar.gz: f2675721e13770fafbd540b82ad6d5b9f33efd1ede32e080b26450d2b5088177b831d985e52bd24aa9f9dbcfdb9ae2ac37df74b0755c756de2a9bdd7a148ba6c
|
data/README.md
CHANGED
|
@@ -11,14 +11,15 @@ Fast C extension for grouping monotonic sequences in Ruby arrays. Groups consecu
|
|
|
11
11
|
- 💎 **Ruby-friendly**: Seamless integration as Array method
|
|
12
12
|
- 🚀 **Optimized Date Processing**: Special fast path for Date objects with cached Julian Day calculations
|
|
13
13
|
|
|
14
|
-
## Recent Updates (v1.0.
|
|
14
|
+
## Recent Updates (v1.0.3 - Stable)
|
|
15
15
|
|
|
16
|
-
### Bug Fixes
|
|
17
|
-
- **Critical**: Fixed
|
|
18
|
-
-
|
|
16
|
+
### Bug Fixes (v1.0.3)
|
|
17
|
+
- **Critical**: Fixed `group_monotonic` method not being available on Array class in external applications
|
|
18
|
+
- Properly encapsulated the method in Array class through C extension
|
|
19
19
|
|
|
20
|
-
###
|
|
21
|
-
- **
|
|
20
|
+
### Previous Updates (v1.0.2)
|
|
21
|
+
- **Critical**: Fixed first element being incorrectly skipped in all processing paths
|
|
22
|
+
- **2x faster Date processing**: Optimized by caching Julian Day Numbers
|
|
22
23
|
- Faster array access using `RARRAY_AREF` macro
|
|
23
24
|
- Better memory preallocation
|
|
24
25
|
- Improved Date subclass handling (DateTime, custom Date classes)
|
|
@@ -42,15 +43,15 @@ require 'monotonic_grouper'
|
|
|
42
43
|
|
|
43
44
|
# Basic integer usage
|
|
44
45
|
[1, 2, 3, 4, 5, 10, 11, 12].group_monotonic(3)
|
|
45
|
-
# => [
|
|
46
|
+
# => [1..5, 10..12]
|
|
46
47
|
|
|
47
48
|
# With singles (sequences shorter than min_range_size)
|
|
48
49
|
[1, 2, 3, 4, 7, 9, 10, 11, 12].group_monotonic(3)
|
|
49
|
-
# => [
|
|
50
|
+
# => [1..4, 7, 9, 10..12]
|
|
50
51
|
|
|
51
52
|
# Custom minimum range size
|
|
52
53
|
[1, 2, 3, 5, 6, 8].group_monotonic(2)
|
|
53
|
-
# => [
|
|
54
|
+
# => [1..3, 5..6, 8]
|
|
54
55
|
|
|
55
56
|
# Works with Dates
|
|
56
57
|
require 'date'
|
|
@@ -63,12 +64,12 @@ dates = [
|
|
|
63
64
|
Date.new(2024, 1, 7)
|
|
64
65
|
]
|
|
65
66
|
dates.group_monotonic(3)
|
|
66
|
-
# => [Date.new(2024, 1,
|
|
67
|
+
# => [Date.new(2024, 1, 1)..Date.new(2024, 1, 3),
|
|
67
68
|
# Date.new(2024, 1, 5)..Date.new(2024, 1, 7)]
|
|
68
69
|
|
|
69
70
|
# Works with characters
|
|
70
71
|
['a', 'b', 'c', 'd', 'f', 'g', 'h'].group_monotonic(3)
|
|
71
|
-
# => ['
|
|
72
|
+
# => ['a'..'d', 'f'..'h']
|
|
72
73
|
```
|
|
73
74
|
|
|
74
75
|
## API
|
|
@@ -5,198 +5,220 @@
|
|
|
5
5
|
#define RB_BIGNUM_TYPE_P(obj) (RB_TYPE_P((obj), T_BIGNUM))
|
|
6
6
|
#endif
|
|
7
7
|
|
|
8
|
+
static inline void seal_array_len(VALUE ary, VALUE *buf, long pos) {
|
|
9
|
+
if (pos == 0)
|
|
10
|
+
return;
|
|
11
|
+
rb_ary_store(ary, pos - 1, buf[pos - 1]);
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
static VALUE rb_mMonotonicGrouper;
|
|
9
15
|
static VALUE rb_cDate;
|
|
10
16
|
static ID id_succ;
|
|
11
|
-
static ID id_eq;
|
|
12
17
|
static ID id_jd;
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
{
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return bv == av + 1;
|
|
21
|
-
}
|
|
22
|
-
return 0;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
static inline int
|
|
26
|
-
is_next_in_sequence_generic(VALUE a, VALUE b)
|
|
27
|
-
{
|
|
28
|
-
VALUE succ_a = rb_funcall(a, id_succ, 0);
|
|
29
|
-
return RTEST(rb_funcall(b, id_eq, 1, succ_a));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
static void
|
|
33
|
-
add_group_to_result_integer(VALUE result, VALUE group_start, VALUE group_end, long size, long min_range_size)
|
|
34
|
-
{
|
|
35
|
-
if (size >= min_range_size) {
|
|
36
|
-
VALUE range = rb_range_new(group_start, group_end, 0);
|
|
37
|
-
rb_ary_push(result, range);
|
|
38
|
-
} else {
|
|
39
|
-
long j;
|
|
40
|
-
long start_val = FIX2LONG(group_start);
|
|
41
|
-
for (j = 0; j < size; j++) {
|
|
42
|
-
rb_ary_push(result, LONG2FIX(start_val + j));
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
static void
|
|
48
|
-
add_group_to_result_date(VALUE result, VALUE group_start, VALUE group_end, long size, long min_range_size)
|
|
49
|
-
{
|
|
50
|
-
if (size >= min_range_size) {
|
|
51
|
-
VALUE range = rb_range_new(group_start, group_end, 0);
|
|
52
|
-
rb_ary_push(result, range);
|
|
53
|
-
} else {
|
|
54
|
-
long j;
|
|
55
|
-
VALUE curr = group_start;
|
|
56
|
-
rb_ary_push(result, curr);
|
|
57
|
-
for (j = 1; j < size; j++) {
|
|
58
|
-
curr = rb_funcall(curr, id_succ, 0);
|
|
59
|
-
rb_ary_push(result, curr);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
19
|
+
#define CHECK_ARRAY_MUTATION(ary, expected_len) \
|
|
20
|
+
do { \
|
|
21
|
+
if (RARRAY_LEN(ary) != (expected_len)) { \
|
|
22
|
+
rb_raise(rb_eRuntimeError, "array was modified during iteration"); \
|
|
23
|
+
} \
|
|
24
|
+
} while (0)
|
|
63
25
|
|
|
64
|
-
static
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (size >= min_range_size) {
|
|
68
|
-
VALUE range = rb_range_new(group_start, group_end, 0);
|
|
69
|
-
rb_ary_push(result, range);
|
|
70
|
-
} else {
|
|
71
|
-
long j;
|
|
72
|
-
VALUE curr = group_start;
|
|
73
|
-
rb_ary_push(result, curr);
|
|
74
|
-
for (j = 1; j < size; j++) {
|
|
75
|
-
curr = rb_funcall(curr, id_succ, 0);
|
|
76
|
-
rb_ary_push(result, curr);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
26
|
+
static VALUE process_fixnum_array(VALUE self, long len, long min_range_size) {
|
|
27
|
+
const VALUE *ptr = RARRAY_CONST_PTR(self);
|
|
28
|
+
const VALUE *end_ptr = ptr + len;
|
|
80
29
|
|
|
81
|
-
static VALUE
|
|
82
|
-
process_integer_array(VALUE self, long len, long min_range_size)
|
|
83
|
-
{
|
|
84
30
|
VALUE result = rb_ary_new_capa(len);
|
|
85
|
-
VALUE
|
|
86
|
-
|
|
87
|
-
VALUE group_end = first_elem;
|
|
88
|
-
VALUE prev_value = first_elem;
|
|
89
|
-
long current_size = 1;
|
|
90
|
-
long i;
|
|
31
|
+
VALUE *out = RARRAY_PTR(result);
|
|
32
|
+
long pos = 0;
|
|
91
33
|
|
|
92
|
-
|
|
93
|
-
|
|
34
|
+
long group_start = FIX2LONG(*ptr);
|
|
35
|
+
long prev = group_start;
|
|
36
|
+
const VALUE *p;
|
|
37
|
+
|
|
38
|
+
for (p = ptr + 1; p < end_ptr; p++) {
|
|
39
|
+
VALUE raw = *p;
|
|
94
40
|
|
|
95
|
-
if (!FIXNUM_P(
|
|
41
|
+
if (!FIXNUM_P(raw)) {
|
|
96
42
|
rb_raise(rb_eTypeError, "All elements must be of the same type");
|
|
97
43
|
}
|
|
98
44
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
45
|
+
long curr = FIX2LONG(raw);
|
|
46
|
+
|
|
47
|
+
if (curr == prev + 1) {
|
|
48
|
+
prev = curr;
|
|
102
49
|
} else {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
50
|
+
long size = prev - group_start + 1;
|
|
51
|
+
if (size >= min_range_size) {
|
|
52
|
+
out[pos++] = rb_range_new(LONG2FIX(group_start), LONG2FIX(prev), 0);
|
|
53
|
+
} else {
|
|
54
|
+
long v;
|
|
55
|
+
for (v = group_start; v <= prev; v++) {
|
|
56
|
+
out[pos++] = LONG2FIX(v);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
group_start = curr;
|
|
60
|
+
prev = curr;
|
|
107
61
|
}
|
|
108
|
-
|
|
109
|
-
prev_value = curr_value;
|
|
110
62
|
}
|
|
111
63
|
|
|
112
|
-
|
|
64
|
+
{
|
|
65
|
+
long size = prev - group_start + 1;
|
|
66
|
+
if (size >= min_range_size) {
|
|
67
|
+
out[pos++] = rb_range_new(LONG2FIX(group_start), LONG2FIX(prev), 0);
|
|
68
|
+
} else {
|
|
69
|
+
long v;
|
|
70
|
+
for (v = group_start; v <= prev; v++) {
|
|
71
|
+
out[pos++] = LONG2FIX(v);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
113
75
|
|
|
76
|
+
seal_array_len(result, out, pos);
|
|
114
77
|
return result;
|
|
115
78
|
}
|
|
116
79
|
|
|
117
|
-
static VALUE
|
|
118
|
-
process_date_array(VALUE self, long len, long min_range_size, VALUE first_elem)
|
|
119
|
-
{
|
|
80
|
+
static VALUE process_date_array(VALUE self, long len, long min_range_size, VALUE first_elem) {
|
|
120
81
|
VALUE result = rb_ary_new_capa(len);
|
|
82
|
+
VALUE *out = RARRAY_PTR(result);
|
|
83
|
+
long pos = 0;
|
|
84
|
+
|
|
121
85
|
VALUE group_start = first_elem;
|
|
122
|
-
VALUE
|
|
86
|
+
VALUE prev_value = first_elem;
|
|
123
87
|
VALUE first_class = CLASS_OF(first_elem);
|
|
124
|
-
long current_size = 1;
|
|
125
|
-
long i;
|
|
126
88
|
long prev_jd = NUM2LONG(rb_funcall(first_elem, id_jd, 0));
|
|
89
|
+
long group_start_jd = prev_jd;
|
|
90
|
+
long i;
|
|
127
91
|
|
|
128
92
|
for (i = 1; i < len; i++) {
|
|
93
|
+
CHECK_ARRAY_MUTATION(self, len);
|
|
94
|
+
|
|
129
95
|
VALUE curr_value = RARRAY_AREF(self, i);
|
|
96
|
+
|
|
130
97
|
if (CLASS_OF(curr_value) != first_class) {
|
|
131
98
|
rb_raise(rb_eTypeError, "All elements must be of the same type");
|
|
132
99
|
}
|
|
133
100
|
|
|
134
101
|
long curr_jd = NUM2LONG(rb_funcall(curr_value, id_jd, 0));
|
|
102
|
+
CHECK_ARRAY_MUTATION(self, len);
|
|
135
103
|
|
|
136
104
|
if (curr_jd == prev_jd + 1) {
|
|
137
|
-
|
|
138
|
-
|
|
105
|
+
prev_value = curr_value;
|
|
106
|
+
prev_jd = curr_jd;
|
|
139
107
|
} else {
|
|
140
|
-
|
|
108
|
+
long size = prev_jd - group_start_jd + 1;
|
|
109
|
+
if (size >= min_range_size) {
|
|
110
|
+
out[pos++] = rb_range_new(group_start, prev_value, 0);
|
|
111
|
+
} else {
|
|
112
|
+
VALUE curr = group_start;
|
|
113
|
+
out[pos++] = curr;
|
|
114
|
+
long j;
|
|
115
|
+
for (j = 1; j < size; j++) {
|
|
116
|
+
curr = rb_funcall(curr, id_succ, 0);
|
|
117
|
+
CHECK_ARRAY_MUTATION(self, len);
|
|
118
|
+
out[pos++] = curr;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
141
121
|
group_start = curr_value;
|
|
142
|
-
|
|
143
|
-
|
|
122
|
+
group_start_jd = curr_jd;
|
|
123
|
+
prev_value = curr_value;
|
|
124
|
+
prev_jd = curr_jd;
|
|
144
125
|
}
|
|
145
|
-
|
|
146
|
-
prev_jd = curr_jd;
|
|
147
126
|
}
|
|
148
127
|
|
|
149
|
-
|
|
128
|
+
{
|
|
129
|
+
long size = prev_jd - group_start_jd + 1;
|
|
130
|
+
if (size >= min_range_size) {
|
|
131
|
+
out[pos++] = rb_range_new(group_start, prev_value, 0);
|
|
132
|
+
} else {
|
|
133
|
+
VALUE curr = group_start;
|
|
134
|
+
out[pos++] = curr;
|
|
135
|
+
long j;
|
|
136
|
+
for (j = 1; j < size; j++) {
|
|
137
|
+
curr = rb_funcall(curr, id_succ, 0);
|
|
138
|
+
CHECK_ARRAY_MUTATION(self, len);
|
|
139
|
+
out[pos++] = curr;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
150
143
|
|
|
144
|
+
seal_array_len(result, out, pos);
|
|
151
145
|
return result;
|
|
152
146
|
}
|
|
153
147
|
|
|
154
|
-
static VALUE
|
|
155
|
-
process_generic_array(VALUE self, long len, long min_range_size, VALUE first_elem)
|
|
156
|
-
{
|
|
148
|
+
static VALUE process_generic_array(VALUE self, long len, long min_range_size, VALUE first_elem) {
|
|
157
149
|
VALUE result = rb_ary_new_capa(len);
|
|
150
|
+
VALUE *out = RARRAY_PTR(result);
|
|
151
|
+
long pos = 0;
|
|
152
|
+
|
|
158
153
|
VALUE group_start = first_elem;
|
|
159
|
-
VALUE group_end = first_elem;
|
|
160
154
|
VALUE prev_value = first_elem;
|
|
161
155
|
VALUE first_class = CLASS_OF(first_elem);
|
|
162
156
|
long current_size = 1;
|
|
163
157
|
long i;
|
|
164
158
|
|
|
165
159
|
for (i = 1; i < len; i++) {
|
|
160
|
+
CHECK_ARRAY_MUTATION(self, len);
|
|
161
|
+
|
|
166
162
|
VALUE curr_value = RARRAY_AREF(self, i);
|
|
163
|
+
|
|
167
164
|
if (CLASS_OF(curr_value) != first_class) {
|
|
168
165
|
rb_raise(rb_eTypeError, "All elements must be of the same type");
|
|
169
166
|
}
|
|
170
167
|
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
VALUE succ_prev = rb_funcall(prev_value, id_succ, 0);
|
|
169
|
+
CHECK_ARRAY_MUTATION(self, len);
|
|
170
|
+
|
|
171
|
+
if (RTEST(rb_equal(curr_value, succ_prev))) {
|
|
173
172
|
current_size++;
|
|
174
173
|
} else {
|
|
175
|
-
|
|
174
|
+
if (current_size >= min_range_size) {
|
|
175
|
+
out[pos++] = rb_range_new(group_start, prev_value, 0);
|
|
176
|
+
} else {
|
|
177
|
+
VALUE curr = group_start;
|
|
178
|
+
out[pos++] = curr;
|
|
179
|
+
long j;
|
|
180
|
+
for (j = 1; j < current_size; j++) {
|
|
181
|
+
curr = rb_funcall(curr, id_succ, 0);
|
|
182
|
+
CHECK_ARRAY_MUTATION(self, len);
|
|
183
|
+
out[pos++] = curr;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
176
186
|
group_start = curr_value;
|
|
177
|
-
group_end = curr_value;
|
|
178
187
|
current_size = 1;
|
|
179
188
|
}
|
|
180
189
|
|
|
181
190
|
prev_value = curr_value;
|
|
182
191
|
}
|
|
183
192
|
|
|
184
|
-
|
|
193
|
+
if (current_size >= min_range_size) {
|
|
194
|
+
out[pos++] = rb_range_new(group_start, prev_value, 0);
|
|
195
|
+
} else {
|
|
196
|
+
VALUE curr = group_start;
|
|
197
|
+
out[pos++] = curr;
|
|
198
|
+
long j;
|
|
199
|
+
for (j = 1; j < current_size; j++) {
|
|
200
|
+
curr = rb_funcall(curr, id_succ, 0);
|
|
201
|
+
CHECK_ARRAY_MUTATION(self, len);
|
|
202
|
+
out[pos++] = curr;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
185
205
|
|
|
206
|
+
seal_array_len(result, out, pos);
|
|
186
207
|
return result;
|
|
187
208
|
}
|
|
188
209
|
|
|
189
|
-
static VALUE
|
|
190
|
-
rb_array_group_monotonic(int argc, VALUE *argv, VALUE self)
|
|
191
|
-
{
|
|
192
|
-
VALUE min_range_size_val;
|
|
210
|
+
static VALUE rb_array_group_monotonic(int argc, VALUE *argv, VALUE self) {
|
|
193
211
|
long min_range_size;
|
|
194
212
|
long len;
|
|
195
213
|
VALUE first_elem;
|
|
196
|
-
VALUE first_class;
|
|
197
214
|
|
|
198
|
-
|
|
199
|
-
|
|
215
|
+
if (argc == 0) {
|
|
216
|
+
min_range_size = 3;
|
|
217
|
+
} else if (argc == 1) {
|
|
218
|
+
min_range_size = NUM2LONG(argv[0]);
|
|
219
|
+
} else {
|
|
220
|
+
rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 0..1)", argc);
|
|
221
|
+
}
|
|
200
222
|
|
|
201
223
|
if (min_range_size < 1) {
|
|
202
224
|
rb_raise(rb_eArgError, "min_range_size must be at least 1");
|
|
@@ -209,10 +231,9 @@ rb_array_group_monotonic(int argc, VALUE *argv, VALUE self)
|
|
|
209
231
|
}
|
|
210
232
|
|
|
211
233
|
first_elem = RARRAY_AREF(self, 0);
|
|
212
|
-
first_class = CLASS_OF(first_elem);
|
|
213
234
|
|
|
214
|
-
if (FIXNUM_P(first_elem)
|
|
215
|
-
return
|
|
235
|
+
if (FIXNUM_P(first_elem)) {
|
|
236
|
+
return process_fixnum_array(self, len, min_range_size);
|
|
216
237
|
}
|
|
217
238
|
|
|
218
239
|
if (rb_cDate != Qnil && rb_obj_is_kind_of(first_elem, rb_cDate)) {
|
|
@@ -226,18 +247,13 @@ rb_array_group_monotonic(int argc, VALUE *argv, VALUE self)
|
|
|
226
247
|
return process_generic_array(self, len, min_range_size, first_elem);
|
|
227
248
|
}
|
|
228
249
|
|
|
229
|
-
static VALUE
|
|
230
|
-
get_date_class(VALUE obj)
|
|
231
|
-
{
|
|
250
|
+
static VALUE get_date_class(VALUE obj) {
|
|
232
251
|
return rb_const_get(obj, rb_intern("Date"));
|
|
233
252
|
}
|
|
234
253
|
|
|
235
|
-
void
|
|
236
|
-
Init_monotonic_grouper(void)
|
|
237
|
-
{
|
|
254
|
+
void Init_monotonic_grouper(void) {
|
|
238
255
|
int state = 0;
|
|
239
256
|
id_succ = rb_intern("succ");
|
|
240
|
-
id_eq = rb_intern("==");
|
|
241
257
|
id_jd = rb_intern("jd");
|
|
242
258
|
rb_mMonotonicGrouper = rb_define_module("MonotonicGrouper");
|
|
243
259
|
rb_cDate = rb_protect(get_date_class, rb_cObject, &state);
|
|
Binary file
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: monotonic_grouper
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Hajdarov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Groups consecutive monotonic sequences in arrays into ranges. Supports
|
|
14
14
|
any Comparable type with succ method.
|
|
@@ -44,7 +44,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
45
|
version: '0'
|
|
46
46
|
requirements: []
|
|
47
|
-
rubygems_version: 3.
|
|
47
|
+
rubygems_version: 3.3.27
|
|
48
48
|
signing_key:
|
|
49
49
|
specification_version: 4
|
|
50
50
|
summary: Fast C extension for grouping monotonic sequences
|