ruby-fizzbuzz 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 : */