ruby-fizzbuzz 0.0.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.
@@ -0,0 +1,71 @@
1
+ /* :enddoc: */
2
+
3
+ /*
4
+ * common.h
5
+ *
6
+ * Copyright 2012-2013 Krzysztof Wilczynski
7
+ *
8
+ * Licensed under the Apache License, Version 2.0 (the "License");
9
+ * you may not use this file except in compliance with the License.
10
+ * You may obtain a copy of the License at
11
+ *
12
+ * http://www.apache.org/licenses/LICENSE-2.0
13
+ *
14
+ * Unless required by applicable law or agreed to in writing, software
15
+ * distributed under the License is distributed on an "AS IS" BASIS,
16
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ * See the License for the specific language governing permissions and
18
+ * limitations under the License.
19
+ */
20
+
21
+ #if !defined(_COMMON_H)
22
+ #define _COMMON_H 1
23
+
24
+ #include <stdint.h>
25
+ #include <ruby.h>
26
+
27
+ #if defined(__cplusplus)
28
+ extern "C" {
29
+ #endif
30
+
31
+ #if !(defined(INT8_MIN) || defined(INT8_MAX))
32
+ typedef signed char int8_t;
33
+ #endif
34
+
35
+ #if !(defined(UINT8_MIN) || defined(UINT8_MAX))
36
+ typedef unsigned char uint8_t;
37
+ #endif
38
+
39
+ #if defined(UNUSED)
40
+ # undef(UNUSED)
41
+ #endif
42
+
43
+ #define UNUSED(x) (void)(x)
44
+
45
+ #if !defined(CSTR2RVAL)
46
+ # define CSTR2RVAL(x) ((x) == NULL ? Qnil : rb_str_new2(x))
47
+ #endif
48
+
49
+ #if !defined(RVAL2CBOOL)
50
+ # define RVAL2CBOOL(x) (RTEST(x))
51
+ #endif
52
+
53
+ #if !defined(CBOOL2RVAL)
54
+ # define CBOOL2RVAL(x) ((x) ? Qtrue : Qfalse)
55
+ #endif
56
+
57
+ #if !defined(HAVE_LONG_LONG)
58
+ # define TYPE2NUM LONG2NUM
59
+ # define NUM2TYPE NUM2LONG
60
+ #else
61
+ # define TYPE2NUM LL2NUM
62
+ # define NUM2TYPE NUM2LL
63
+ #endif
64
+
65
+ #if defined(__cplusplus)
66
+ }
67
+ #endif
68
+
69
+ #endif /* _COMMON_H */
70
+
71
+ /* vim: set ts=8 sw=4 sts=2 et : */
@@ -0,0 +1,40 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ # :enddoc:
4
+
5
+ #
6
+ # extconf.rb
7
+ #
8
+ # Copyright 2012-2013 Krzysztof Wilczynski
9
+ #
10
+ # Licensed under the Apache License, Version 2.0 (the "License");
11
+ # you may not use this file except in compliance with the License.
12
+ # You may obtain a copy of the License at
13
+ #
14
+ # http://www.apache.org/licenses/LICENSE-2.0
15
+ #
16
+ # Unless required by applicable law or agreed to in writing, software
17
+ # distributed under the License is distributed on an "AS IS" BASIS,
18
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ # See the License for the specific language governing permissions and
20
+ # limitations under the License.
21
+ #
22
+
23
+ require 'mkmf'
24
+
25
+ RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
26
+
27
+ $LDFLAGS << " %s" % ENV['LDFLAGS'] if ENV['LDFLAGS']
28
+
29
+ $CFLAGS << " %s" % ENV['CFLAGS'] if ENV['CFLAGS']
30
+ $CFLAGS << ' -std=c99 -g -Wall -Wextra -pedantic'
31
+
32
+ have_header('ruby.h') or missing('ruby.h')
33
+
34
+ dir_config('fizzbuzz')
35
+
36
+ create_header
37
+ create_makefile('fizzbuzz/fizzbuzz')
38
+
39
+ # vim: set ts=2 sw=2 sts=2 et :
40
+ # encoding: utf-8
@@ -0,0 +1,473 @@
1
+ /* :stopdoc: */
2
+
3
+ /*
4
+ * fizzbuzz.c
5
+ *
6
+ * Copyright 2012-2013 Krzysztof Wilczynski
7
+ *
8
+ * Licensed under the Apache License, Version 2.0 (the "License");
9
+ * you may not use this file except in compliance with the License.
10
+ * You may obtain a copy of the License at
11
+ *
12
+ * http://www.apache.org/licenses/LICENSE-2.0
13
+ *
14
+ * Unless required by applicable law or agreed to in writing, software
15
+ * distributed under the License is distributed on an "AS IS" BASIS,
16
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ * See the License for the specific language governing permissions and
18
+ * limitations under the License.
19
+ */
20
+
21
+ /* :startdoc: */
22
+
23
+ #include <fizzbuzz.h>
24
+
25
+ ID id_at_start, id_at_stop;
26
+ VALUE rb_cFizzBuzz = Qnil;
27
+
28
+ void Init_fizzbuzz(void);
29
+
30
+ static VALUE fizzbuzz_evaluate(VALUE value);
31
+ static VALUE fizzbuzz_values(VALUE object, return_type_t type,
32
+ direction_t direction);
33
+
34
+ /*
35
+ * call-seq:
36
+ * FizzBuzz.new( start, stop ) -> self
37
+ *
38
+ * Returns a new _FizzBuzz_.
39
+ *
40
+ * The given +start+ and +stop+ values must be of an integer type and
41
+ * will establish a +range+ within which calculation of any relevant
42
+ * _FizzBuzz_ results will have place.
43
+ *
44
+ * Example:
45
+ *
46
+ * fb = FizzBuzz.new(1, 100) #=> #<FizzBuzz:0xb6fd3b38 @stop=100, @start=1>
47
+ * fb = FizzBuzz.new(-100,-1) #=> #<FizzBuzz:0xb72d5700 @stop=-1, @start=-100>
48
+ * fb = FizzBuzz.new(-15, 15) #=> #<FizzBuzz:0xb6fd0460 @stop=15, @start=-15>
49
+ *
50
+ * The given value of +stop+ must always be greater than or equal to the
51
+ * given value of +start+, otherwise raises an +ArgumentError+ exception.
52
+ *
53
+ * Will raise a +TypeError+ exception if given value of either +start+
54
+ * or +stop+ is not an integer type.
55
+ *
56
+ * See also:
57
+ *
58
+ * FizzBuzz::fizzbuzz
59
+ */
60
+ VALUE
61
+ rb_fb_initialize(int argc, VALUE *argv, VALUE object)
62
+ {
63
+ VALUE start, stop;
64
+
65
+ rb_scan_args(argc, argv, "20", &start, &stop);
66
+
67
+ CHECK_TYPE(start, errors[E_INVALID_START_TYPE]);
68
+ CHECK_TYPE(stop, errors[E_INVALID_STOP_TYPE]);
69
+
70
+ CHECK_BOUNDARY(start, stop, errors[E_BAD_VALUE_START]);
71
+
72
+ rb_ivar_set(object, id_at_start, start);
73
+ rb_ivar_set(object, id_at_stop, stop);
74
+
75
+ return object;
76
+ }
77
+
78
+ /*
79
+ * call-seq:
80
+ * fizzfuzz.start -> integer
81
+ *
82
+ * Returns the current value for +start+.
83
+ *
84
+ * Example:
85
+ *
86
+ * fb = FizzBuzz.new(1, 100) #=> #<FizzBuzz:0xf726b48c @stop=100, @start=1>
87
+ * fb.start #=> 1
88
+ */
89
+ VALUE
90
+ rb_fb_get_start(VALUE object)
91
+ {
92
+ return rb_ivar_get(object, id_at_start);
93
+ }
94
+
95
+ /*
96
+ * call-seq:
97
+ * fizzfuzz.start= (integer) -> integer
98
+ *
99
+ * Sets the current value of +start+ if given new value is lower or equal
100
+ * to the current value of +stop+, or raises an +ArgumentError+ exception
101
+ * otherwise.
102
+ *
103
+ * Examples:
104
+ *
105
+ * fb = FizzBuzz.new(1, 100) #=> #<FizzBuzz:0xf726f03c @stop=100, @start=1>
106
+ * fb.start #=> 1
107
+ * fb.start = 15 #=> 15
108
+ * fb.start #=> 15
109
+ *
110
+ * Will raise a +TypeError+ exception if given value is not an integer type.
111
+ */
112
+ VALUE
113
+ rb_fb_set_start(VALUE object, VALUE value)
114
+ {
115
+ VALUE stop = rb_ivar_get(object, id_at_stop);
116
+
117
+ CHECK_TYPE(value, errors[E_INVALID_START_TYPE]);
118
+ CHECK_BOUNDARY(value, stop, errors[E_BAD_VALUE_START]);
119
+
120
+ return rb_ivar_set(object, id_at_start, value);
121
+ }
122
+
123
+ /*
124
+ * call-seq:
125
+ * fizzfuzz.stop -> integer
126
+ *
127
+ * Returns the current value for +stop+.
128
+ *
129
+ * Example:
130
+ *
131
+ * fb = FizzBuzz.new(1, 100) #=> #<FizzBuzz:0xf7272bec @stop=100, @start=1>
132
+ * fb.stop #=> 100
133
+ */
134
+ VALUE
135
+ rb_fb_get_stop(VALUE object)
136
+ {
137
+ return rb_ivar_get(object, id_at_stop);
138
+ }
139
+
140
+ /*
141
+ * call-seq:
142
+ * fizzfuzz.start= (integer) -> integer
143
+ *
144
+ * Sets the current value of +stop+ if given new value is greater or equal
145
+ * to the current value of +start+, or raises an +ArgumentError+ exception
146
+ * otherwise.
147
+ *
148
+ * Example:
149
+ *
150
+ * fb = FizzBuzz.new(1, 100) #=> #<FizzBuzz:0xf727679c @stop=100, @start=1>
151
+ * fb.stop #=> 100
152
+ * fb.stop = 15 #=> 15
153
+ * fb.stop #=> 15
154
+ *
155
+ * Will raise a +TypeError+ exception if given value is not an integer type.
156
+ */
157
+ VALUE
158
+ rb_fb_set_stop(VALUE object, VALUE value)
159
+ {
160
+ VALUE start = rb_ivar_get(object, id_at_start);
161
+
162
+ CHECK_TYPE(value, errors[E_INVALID_STOP_TYPE]);
163
+ CHECK_BOUNDARY(start, value, errors[E_BAD_VALUE_STOP]);
164
+
165
+ return rb_ivar_set(object, id_at_stop, value);
166
+ }
167
+
168
+ /*
169
+ * call-seq:
170
+ * fizzbuzz.to_a -> array
171
+ *
172
+ * Returns an array containing results upon calculating an appropriate
173
+ * values for a given range from +start+ to +stop+.
174
+ *
175
+ * Example:
176
+ *
177
+ * fb = FizzBuzz.new(1, 15) #=> #<FizzBuzz:0xf727fd60 @stop=15, @start=1>
178
+ * fb.to_a #=> [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]
179
+ *
180
+ * See also: FizzBuzz::fizzbuzz
181
+ */
182
+ VALUE
183
+ rb_fb_array(VALUE object)
184
+ {
185
+ return fizzbuzz_values(object, R_TYPE_ARRAY, D_LOOP_FORWARD);
186
+ }
187
+
188
+ /*
189
+ * call-seq:
190
+ * fizzbuzz.each {|value| block } -> self
191
+ * fizzbuzz.each -> an Enumerator
192
+ *
193
+ * Calls the block once for each subsequent value for a given range
194
+ * from +start+ to +stop+, passing the value as a parameter to the block.
195
+ *
196
+ * If no block is given, an +Enumerator+ is returned instead.
197
+ *
198
+ * Example:
199
+ *
200
+ * fb = FizzBuzz.new(1, 15) #=> #<FizzBuzz:0xf722f8ec @stop=15, @start=1>
201
+ * fb.each {|value| puts "Got #{value}" }
202
+ *
203
+ * Produces:
204
+ *
205
+ * Got 1
206
+ * Got 2
207
+ * Got Fizz
208
+ * Got 4
209
+ * Got Buzz
210
+ * Got Fizz
211
+ * Got 7
212
+ * Got 8
213
+ * Got Fizz
214
+ * Got Buzz
215
+ * Got 11
216
+ * Got Fizz
217
+ * Got 13
218
+ * Got 14
219
+ * Got FizzBuzz
220
+ *
221
+ * See also: FizzBuzz#reverse_each
222
+ */
223
+ VALUE
224
+ rb_fb_enumerator(VALUE object)
225
+ {
226
+ return fizzbuzz_values(object, R_TYPE_ENUMERATOR, D_LOOP_FORWARD);
227
+ }
228
+
229
+ /*
230
+ * call-seq:
231
+ * fizzbuzz.reverse_each {|value| block } -> self
232
+ * fizzbuzz.reverse_each -> an Enumerator
233
+ *
234
+ * Calls the block once for each subsequent value for a given range
235
+ * from +start+ to +stop+ in an <i>reverse order</i>, passing the value
236
+ * as a parameter to the block.
237
+ *
238
+ * Example:
239
+ *
240
+ * If no block is given, an +Enumerator+ is returned instead.
241
+ *
242
+ * fb = FizzBuzz.new(1, 15) #=> #<FizzBuzz:0xb7308664 @stop=15, @start=1>
243
+ * fb.reverse_each {|value| puts "Got #{value}" }
244
+ *
245
+ * Produces:
246
+ *
247
+ * Got FizzBuzz
248
+ * Got 14
249
+ * Got 13
250
+ * Got Fizz
251
+ * Got 11
252
+ * Got Buzz
253
+ * Got Fizz
254
+ * Got 8
255
+ * Got 7
256
+ * Got Fizz
257
+ * Got Buzz
258
+ * Got 4
259
+ * Got Fizz
260
+ * Got 2
261
+ * Got 1
262
+ *
263
+ * See also: FizzBuzz#each
264
+ */
265
+ VALUE
266
+ rb_fb_reverse_enumerator(VALUE object)
267
+ {
268
+ return fizzbuzz_values(object, R_TYPE_ENUMERATOR, D_LOOP_REVERSE);
269
+ }
270
+
271
+ /*
272
+ * call-seq:
273
+ * FizzBuzz.is_fizz?( integer ) -> true or false
274
+ *
275
+ * Returns +true+ if a given integer value is divisible by *three* (given
276
+ * value is a _Fizz_), or +false+ otherwise.
277
+ *
278
+ * Example:
279
+ *
280
+ * FizzBuzz.is_fizz?(3) #=> true
281
+ * FizzBuzz.is_fizz?(5) #=> false
282
+ * FizzBuzz.is_fizz?(15) #=> false
283
+ *
284
+ * Will raise a +TypeError+ exception if given value is not an integer type.
285
+ *
286
+ * See also: FizzBuzz::[]
287
+ */
288
+ VALUE
289
+ rb_fb_is_fizz(VALUE object, VALUE value)
290
+ {
291
+ UNUSED(object);
292
+ CHECK_TYPE(value, errors[E_INVALID_TYPE]);
293
+
294
+ return CBOOL2RVAL(IS_FIZZ(value));
295
+ }
296
+
297
+ /*
298
+ * call-seq:
299
+ * FizzBuzz.is_buzz?( integer ) -> true or false
300
+ *
301
+ * Returns +true+ if a given integer value is divisible by *five* (given
302
+ * value is a _Buzz_), or +false+ otherwise.
303
+ *
304
+ * Example:
305
+ *
306
+ * FizzBuzz.is_buzz?(3) #=> false
307
+ * FizzBuzz.is_buzz?(5) #=> true
308
+ * FizzBuzz.is_buzz?(15) #=> false
309
+ *
310
+ * Will raise a +TypeError+ exception if given value is not an integer type.
311
+ *
312
+ * See also: FizzBuzz::[]
313
+ */
314
+ VALUE
315
+ rb_fb_is_buzz(VALUE object, VALUE value)
316
+ {
317
+ UNUSED(object);
318
+ CHECK_TYPE(value, errors[E_INVALID_TYPE]);
319
+
320
+ return CBOOL2RVAL(IS_BUZZ(value));
321
+ }
322
+
323
+ /*
324
+ * call-seq:
325
+ * FizzBuzz.is_fizzbuzz?( integer ) -> true or false
326
+ *
327
+ * Returns +true+ if a given integer value is divisible by both *three*
328
+ * and *five* (given value is a _FizzBuzz_), or +false+ otherwise.
329
+ *
330
+ * Example:
331
+ *
332
+ * FizzBuzz.is_fizzbuzz?(3) #=> false
333
+ * FizzBuzz.is_fizzbuzz?(5) #=> false
334
+ * FizzBuzz.is_fizzbuzz?(15) #=> true
335
+ *
336
+ * Will raise a +TypeError+ exception if given value is not an integer type.
337
+ *
338
+ * See also: FizzBuzz::[]
339
+ */
340
+ VALUE
341
+ rb_fb_is_fizzbuzz(VALUE object, VALUE value)
342
+ {
343
+ UNUSED(object);
344
+ CHECK_TYPE(value, errors[E_INVALID_TYPE]);
345
+
346
+ return CBOOL2RVAL(IS_FIZZBUZZ(value));
347
+ }
348
+
349
+ /*
350
+ * call-seq:
351
+ * FizzBuzz[ integer ] -> integer or string
352
+ *
353
+ * Returns _Fizz_ if the given value is divisible by *three*, _Buzz_
354
+ * if the given value is divisible by *five* and _FizzBuzz_ if the
355
+ * given value is divisible by both *three* and *five*, or the given
356
+ * integer value otherwise.
357
+ *
358
+ * Example:
359
+ *
360
+ * FizzBuzz[1] #=> 1
361
+ * FizzBuzz[3] #=> "Fizz"
362
+ * FizzBuzz[5] #=> "Buzz"
363
+ * FizzBuzz[15] #=> "FizzBuzz"
364
+ *
365
+ * Will raise a +TypeError+ exception if given value is not an integer type.
366
+ *
367
+ * See also: FizzBuzz::is_fizz?, FizzBuzz::is_buzz? and FizzBuzz::is_fizzbuzz?
368
+ */
369
+ VALUE
370
+ rb_fb_square(VALUE object, VALUE value)
371
+ {
372
+ UNUSED(object);
373
+ CHECK_TYPE(value, errors[E_INVALID_TYPE]);
374
+
375
+ return fizzbuzz_evaluate(value);
376
+ }
377
+
378
+ /* :enddoc: */
379
+
380
+ static VALUE
381
+ fizzbuzz_evaluate(VALUE value)
382
+ {
383
+ int8_t score;
384
+
385
+ VALUE result = Qnil;
386
+
387
+ if (ZERO_P(value)) {
388
+ return value;
389
+ }
390
+
391
+ score = SCORE_VALUE(value);
392
+
393
+ switch(score) {
394
+ case 0:
395
+ result = value;
396
+ break;
397
+ case 1:
398
+ result = CSTR2RVAL(words[score - 1]);
399
+ break;
400
+ case 2:
401
+ result = CSTR2RVAL(words[score - 1]);
402
+ break;
403
+ case 3:
404
+ result = CSTR2RVAL(words[score - 1]);
405
+ break;
406
+ }
407
+
408
+ return result;
409
+ }
410
+
411
+ static VALUE
412
+ fizzbuzz_values(VALUE object, return_type_t type, direction_t direction)
413
+ {
414
+ VALUE i = Qnil;
415
+
416
+ VALUE array = Qnil;
417
+ VALUE value = Qnil;
418
+
419
+ VALUE start = rb_ivar_get(object, id_at_start);
420
+ VALUE stop = rb_ivar_get(object, id_at_stop);
421
+
422
+ if (WANT_ARRAY(type)) {
423
+ array = rb_ary_new();
424
+ }
425
+ else {
426
+ RETURN_ENUMERATOR(object, 0, 0);
427
+ }
428
+
429
+ if (LOOP_FORWARD(direction)) {
430
+ for (i = start; LESS_EQUAL(i, stop); i = INCREASE(i)) {
431
+ value = fizzbuzz_evaluate(i);
432
+ WANT_ARRAY(type) ? rb_ary_push(array, value) : rb_yield(value);
433
+ }
434
+ }
435
+ else {
436
+ for (i = stop; GREATER_EQUAL(i, start); i = DECREASE(i)) {
437
+ value = fizzbuzz_evaluate(i);
438
+ WANT_ARRAY(type) ? rb_ary_push(array, value) : rb_yield(value);
439
+ }
440
+ }
441
+
442
+ return WANT_ARRAY(type) ? array : object;
443
+ }
444
+
445
+ void
446
+ Init_fizzbuzz(void)
447
+ {
448
+ id_at_start = rb_intern("@start");
449
+ id_at_stop = rb_intern("@stop");
450
+
451
+ rb_cFizzBuzz = rb_define_class("FizzBuzz", rb_cObject);
452
+
453
+ rb_include_module(rb_cFizzBuzz, rb_mEnumerable);
454
+
455
+ rb_define_method(rb_cFizzBuzz, "initialize", RUBY_METHOD_FUNC(rb_fb_initialize), -1);
456
+
457
+ rb_define_method(rb_cFizzBuzz, "start", RUBY_METHOD_FUNC(rb_fb_get_start), 0);
458
+ rb_define_method(rb_cFizzBuzz, "start=", RUBY_METHOD_FUNC(rb_fb_set_start), 1);
459
+ rb_define_method(rb_cFizzBuzz, "stop", RUBY_METHOD_FUNC(rb_fb_get_stop), 0);
460
+ rb_define_method(rb_cFizzBuzz, "stop=", RUBY_METHOD_FUNC(rb_fb_set_stop), 1);
461
+
462
+ rb_define_method(rb_cFizzBuzz, "to_a", RUBY_METHOD_FUNC(rb_fb_array), 0);
463
+ rb_define_method(rb_cFizzBuzz, "each", RUBY_METHOD_FUNC(rb_fb_enumerator), 0);
464
+ rb_define_method(rb_cFizzBuzz, "reverse_each", RUBY_METHOD_FUNC(rb_fb_reverse_enumerator), 0);
465
+
466
+ rb_define_singleton_method(rb_cFizzBuzz, "is_fizz?", RUBY_METHOD_FUNC(rb_fb_is_fizz), 1);
467
+ rb_define_singleton_method(rb_cFizzBuzz, "is_buzz?", RUBY_METHOD_FUNC(rb_fb_is_buzz), 1);
468
+ rb_define_singleton_method(rb_cFizzBuzz, "is_fizzbuzz?", RUBY_METHOD_FUNC(rb_fb_is_fizzbuzz), 1);
469
+
470
+ rb_define_singleton_method(rb_cFizzBuzz, "[]", RUBY_METHOD_FUNC(rb_fb_square), 1);
471
+ }
472
+
473
+ /* vim: set ts=8 sw=4 sts=2 et : */