magician 0.2.1 → 0.3.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.
- data/.travis.yml +6 -8
- data/CHANGELOG.md +33 -5
- data/Gemfile +7 -7
- data/LICENSE.txt +1 -1
- data/README.md +15 -9
- data/VERSION +1 -1
- data/lib/magician/array.rb +133 -92
- data/lib/magician/integer.rb +44 -50
- data/lib/magician/math.rb +167 -116
- data/lib/magician/numeric.rb +38 -26
- data/lib/magician/random.rb +31 -0
- data/lib/magician/shortcuts.rb +5 -2
- data/lib/magician/string.rb +7 -7
- data/magician.gemspec +20 -18
- data/spec/array_spec.rb +87 -70
- data/spec/integer_spec.rb +44 -30
- data/spec/math_spec.rb +90 -72
- data/spec/numeric_spec.rb +36 -18
- data/spec/random_spec.rb +21 -0
- data/spec/shortcuts_spec.rb +7 -5
- data/spec/spec_helper.rb +2 -0
- data/spec/string_spec.rb +10 -9
- metadata +20 -18
data/lib/magician/math.rb
CHANGED
@@ -1,121 +1,172 @@
|
|
1
1
|
# Magician's extensions to the Math module.
|
2
2
|
module Math
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
4
|
+
# If we don't do this, our new methods won't get added onto Math.
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Solves a quadratic formula of the form "ax^2+bx+c=0" for x, where a is not
|
8
|
+
# 0. It asks for the three coefficients of the function (a, b, and c), and
|
9
|
+
# returns the two possible values for x. Complex number results are not
|
10
|
+
# supported yet.
|
11
|
+
#
|
12
|
+
# @param [Numeric] a the first coefficient (must not be 0)
|
13
|
+
# @param [Numeric] b the second coefficient
|
14
|
+
# @param [Numeric] c the third coefficient
|
15
|
+
#
|
16
|
+
# @return [Array] a sorted array of two Floats, the two possible values for x
|
17
|
+
#
|
18
|
+
# @raise [ArgumentError] if a is 0
|
19
|
+
def quadratic(a, b, c)
|
20
|
+
raise ArgumentError, 'a cannot be zero' if a.zero?
|
21
|
+
|
22
|
+
left = -b
|
23
|
+
right = Math.sqrt(b**2 - 4*a*c)
|
24
|
+
bottom = 2*a
|
25
|
+
|
26
|
+
[ (left+right)/bottom, (left-right)/bottom ].sort
|
27
|
+
end
|
28
|
+
|
29
|
+
# The number of size k ordered subsets of a set of size n. Equivalent to
|
30
|
+
# n!/(n-k)!.
|
31
|
+
#
|
32
|
+
# @param [Integer] n the size of the set to pick from
|
33
|
+
# @param [Integer] k the size of the ordered subsets
|
34
|
+
#
|
35
|
+
# @return [Integer] the number of permutations
|
36
|
+
#
|
37
|
+
# @raise [ArgumentError] if either argument is negative, or if n < k
|
38
|
+
def permutations(n, k)
|
39
|
+
raise ArgumentError, 'n cannot be negative' if n < 0
|
40
|
+
raise ArgumentError, 'k cannot be negative' if k < 0
|
41
|
+
raise ArgumentError, 'n must be at least as large as k' if n < k
|
42
|
+
|
43
|
+
n.factorial / (n-k).factorial
|
44
|
+
end
|
45
|
+
|
46
|
+
# The number of size k unordered subsets of a set of size n. Equivalent to
|
47
|
+
# n!/(k!(n-k)!).
|
48
|
+
#
|
49
|
+
# @param [Integer] n the size of the set to pick from
|
50
|
+
# @param [Integer] k the size of the unordered subsets
|
51
|
+
#
|
52
|
+
# @return [Integer] the number of combinations
|
53
|
+
#
|
54
|
+
# @raise [ArgumentError] if either argument is negative, or if n < k
|
55
|
+
def combinations(n, k)
|
56
|
+
raise ArgumentError, 'n cannot be negative' if n < 0
|
57
|
+
raise ArgumentError, 'k cannot be negative' if k < 0
|
58
|
+
raise ArgumentError, 'n must be at least as large as k' if n < k
|
59
|
+
|
60
|
+
n.factorial / (k.factorial * (n-k).factorial)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get the number of steps it takes to get from integer n to 1 using the
|
64
|
+
# Collatz conjecture.
|
65
|
+
#
|
66
|
+
# @see http://en.wikipedia.org/wiki/Collatz_conjecture
|
67
|
+
#
|
68
|
+
# @param [Integer] n the number to put into the Collatz conjecture initially
|
69
|
+
# @param [Integer] depth the number of steps that have passed so far (should
|
70
|
+
# not be modified unless this is being cached carefully)
|
71
|
+
#
|
72
|
+
# @return [Integer] the number of steps it takes to get from integer n to 1
|
73
|
+
# using the Collatz conjecture (the depth)
|
74
|
+
#
|
75
|
+
# @raise [ArgumentError] if n < 1
|
76
|
+
def collatz(n, depth=0)
|
77
|
+
raise ArgumentError, 'n must be at least 1' if n < 1
|
78
|
+
|
79
|
+
if n == 1
|
80
|
+
depth
|
81
|
+
elsif n % 2 == 0
|
82
|
+
depth += 1
|
83
|
+
collatz(n/2, depth)
|
84
|
+
else
|
85
|
+
depth += 1
|
86
|
+
collatz(3*n + 1, depth)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Using the Pythagorean theorem, gets c (the length of the hypotenuse) when a
|
91
|
+
# and b (the lengths of the other sides of a triangle) are given.
|
92
|
+
#
|
93
|
+
# @param [Numeric] a the length of the first side of the triangle
|
94
|
+
# @param [Numeric] b the length of the second side of the triangle
|
95
|
+
#
|
96
|
+
# @return [Float] the length of the hypotenuse of the triangle
|
97
|
+
#
|
98
|
+
# @raise [ArgumentError] if either argument is negative
|
99
|
+
def hypotenuse(a, b)
|
100
|
+
raise ArgumentError, 'a cannot be negative' if a < 0
|
101
|
+
raise ArgumentError, 'b cannot be negative' if b < 0
|
102
|
+
|
103
|
+
Math.sqrt(a**2 + b**2)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns true if the three given numbers are positive integers that form a
|
107
|
+
# Pythagorean triplet (that is, if a^2+b^2=c^2). C must be the last parameter.
|
108
|
+
#
|
109
|
+
# @param [Integer] a the length of the first side of the triangle
|
110
|
+
# @param [Integer] b the length of the second side of the triangle
|
111
|
+
# @param [Integer] c the length of the hypotenuse of the triangle
|
112
|
+
#
|
113
|
+
# @return [Boolean] true if the three numbers form a Pythagorean triplet
|
114
|
+
def triplet?(a, b, c)
|
115
|
+
return false if [a, b, c].any? { |n| n < 1 or not n.class <= Integer }
|
116
|
+
|
117
|
+
a**2 + b**2 == c**2
|
118
|
+
end
|
119
|
+
|
120
|
+
# Calculates a series of Fibonacci numbers of a specified length. Note that
|
121
|
+
# if terms are not passed to this method, it will start generating numbers
|
122
|
+
# with the terms [1, 1].
|
123
|
+
#
|
124
|
+
# @param [Integer] length the length of the Fibonacci series that should be
|
125
|
+
# returned
|
126
|
+
#
|
127
|
+
# @return [Array] a Fibonacci series of Integers with the specified length
|
128
|
+
# (ordered)
|
129
|
+
#
|
130
|
+
# @raise [ArgumentError] if a negative length is given, or if less than two
|
131
|
+
# terms are given
|
132
|
+
def fibs length, terms = [1, 1]
|
133
|
+
raise ArgumentError, 'Length must be at least 0' if length < 0
|
134
|
+
raise ArgumentError, 'At least two terms must be given' if terms.length < 2
|
135
|
+
|
136
|
+
terms << (terms[-2] + terms[-1]) while terms.length < length
|
137
|
+
|
138
|
+
terms.first length
|
139
|
+
end
|
140
|
+
|
141
|
+
# Finds all prime numbers from 1 to a given number n (inclusive) using the
|
142
|
+
# Sieve of Eratosthenes.
|
143
|
+
#
|
144
|
+
# @see http://www.algorithmist.com/index.php/Prime_Sieve_of_Eratosthenes
|
145
|
+
#
|
146
|
+
# @param [Integer] limit the upper limit of all primes to find (inclusive)
|
147
|
+
#
|
148
|
+
# @return [Array] an array of integers containing all discovered primes (in
|
149
|
+
# increasing order)
|
150
|
+
def primes limit
|
151
|
+
# Initialize the array of booleans
|
152
|
+
is_prime = [true] * (limit+1)
|
153
|
+
is_prime[0] = false
|
154
|
+
is_prime[1] = false
|
155
|
+
|
156
|
+
# Check for composite numbers and update the array with results
|
157
|
+
2.upto(Math.sqrt limit).each do |i|
|
158
|
+
if is_prime[i]
|
159
|
+
# Mark all multiples of i as composite
|
160
|
+
2.upto(limit).each do |factor|
|
161
|
+
multiple = i * factor
|
162
|
+
break if multiple > limit
|
163
|
+
is_prime[multiple] = false
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Create an array of prime integers by iterating over the array of booleans
|
169
|
+
1.upto(limit).reduce [] { |primes, i| is_prime[i] ? primes << i : primes }
|
170
|
+
end
|
120
171
|
|
121
172
|
end
|
data/lib/magician/numeric.rb
CHANGED
@@ -1,31 +1,43 @@
|
|
1
1
|
# Magician's extensions to the Numeric class (affects Integers and Floats).
|
2
2
|
class Numeric
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
4
|
+
# Returns true if the number is evenly divisible by n. If n is equal to 0, it
|
5
|
+
# returns false, since numbers cannot be divided by 0 in real number
|
6
|
+
# arithmetic.
|
7
|
+
#
|
8
|
+
# @param [Numeric] n the number that this number (self) should be divided by
|
9
|
+
#
|
10
|
+
# @return [Boolean] true if the number is evenly divisible by n
|
11
|
+
def divisible? n
|
12
|
+
n.zero? ? false : modulo(n).zero?
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
15
|
+
# Performs to_s[selection].to_i on the number. Note that for floats, the
|
16
|
+
# decimal counts as a digit within the string.
|
17
|
+
#
|
18
|
+
# @param [Range] selection the selection/range to get from the number (you can
|
19
|
+
# use anything that works with the [] syntax)
|
20
|
+
#
|
21
|
+
# @return [Integer] substring of the number (using []), converted to an
|
22
|
+
# Integer
|
23
|
+
#
|
24
|
+
# @deprecated Avoid using this any more. It's inaccurate and not very useful.
|
25
|
+
def digits selection
|
26
|
+
to_s[selection].to_i
|
27
|
+
end
|
30
28
|
|
31
|
-
|
29
|
+
# Converts the number from degrees to radians and returns the result.
|
30
|
+
#
|
31
|
+
# @return [Numeric] the number in radians
|
32
|
+
def to_radians
|
33
|
+
self * Math::PI / 180
|
34
|
+
end
|
35
|
+
|
36
|
+
# Converts the number from radians to degrees and returns the result.
|
37
|
+
#
|
38
|
+
# @return [Numeric] the number in degrees
|
39
|
+
def to_degrees
|
40
|
+
self * 180 / Math::PI
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Magician's extensions to the Random class.
|
2
|
+
class Random
|
3
|
+
|
4
|
+
# For all the instance methods defined below, add a class method which invokes
|
5
|
+
# the instance method on DEFAULT.
|
6
|
+
[:boolean, :coin, :die].each do |name|
|
7
|
+
define_singleton_method(name) { DEFAULT.send name }
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns a random boolean (true or false).
|
11
|
+
#
|
12
|
+
# @return [Boolean] true or false
|
13
|
+
def boolean
|
14
|
+
[true, false].sample random: self
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns a random coin toss (heads or tails).
|
18
|
+
#
|
19
|
+
# @return [String] 'heads' or 'tails'
|
20
|
+
def coin
|
21
|
+
['heads', 'tails'].sample random: self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a random die roll (from 1-6).
|
25
|
+
#
|
26
|
+
# @return [Fixnum] a Fixnum from 1-6
|
27
|
+
def die
|
28
|
+
rand 1..6
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/magician/shortcuts.rb
CHANGED
data/lib/magician/string.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# Magician's extensions to the String class.
|
2
2
|
class String
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
# Returns true if the string is a palindrome (meaning it is the same forward
|
5
|
+
# and backward).
|
6
|
+
#
|
7
|
+
# @return [Boolean] true if the string is a palindrome
|
8
|
+
def palindrome?
|
9
|
+
eql? reverse
|
10
|
+
end
|
11
11
|
|
12
12
|
end
|
data/magician.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "magician"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Nicolas McCurdy"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2013-03-18"
|
13
13
|
s.description = "A suite of handy methods for doing calculations in irb."
|
14
14
|
s.email = "thenickperson@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
"lib/magician/integer.rb",
|
32
32
|
"lib/magician/math.rb",
|
33
33
|
"lib/magician/numeric.rb",
|
34
|
+
"lib/magician/random.rb",
|
34
35
|
"lib/magician/shortcuts.rb",
|
35
36
|
"lib/magician/string.rb",
|
36
37
|
"magician.gemspec",
|
@@ -38,6 +39,7 @@ Gem::Specification.new do |s|
|
|
38
39
|
"spec/integer_spec.rb",
|
39
40
|
"spec/math_spec.rb",
|
40
41
|
"spec/numeric_spec.rb",
|
42
|
+
"spec/random_spec.rb",
|
41
43
|
"spec/shortcuts_spec.rb",
|
42
44
|
"spec/spec_helper.rb",
|
43
45
|
"spec/string_spec.rb"
|
@@ -45,34 +47,34 @@ Gem::Specification.new do |s|
|
|
45
47
|
s.homepage = "http://github.com/thenickperson/magician"
|
46
48
|
s.licenses = ["MIT"]
|
47
49
|
s.require_paths = ["lib"]
|
48
|
-
s.rubygems_version = "1.8.
|
50
|
+
s.rubygems_version = "1.8.23"
|
49
51
|
s.summary = "A suite of handy methods for doing calculations in irb."
|
50
52
|
|
51
53
|
if s.respond_to? :specification_version then
|
52
54
|
s.specification_version = 3
|
53
55
|
|
54
56
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
55
|
-
s.add_development_dependency(%q<rspec>, ["~> 2.
|
57
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.13"])
|
56
58
|
s.add_development_dependency(%q<yard>, ["~> 0.7"])
|
57
|
-
s.add_development_dependency(%q<rdoc>, ["~>
|
58
|
-
s.add_development_dependency(%q<bundler>, ["
|
59
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.8
|
60
|
-
s.add_development_dependency(%q<simplecov>, ["
|
59
|
+
s.add_development_dependency(%q<rdoc>, ["~> 4.0"])
|
60
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.2"])
|
61
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8"])
|
62
|
+
s.add_development_dependency(%q<simplecov>, ["~> 0.7"])
|
61
63
|
else
|
62
|
-
s.add_dependency(%q<rspec>, ["~> 2.
|
64
|
+
s.add_dependency(%q<rspec>, ["~> 2.13"])
|
63
65
|
s.add_dependency(%q<yard>, ["~> 0.7"])
|
64
|
-
s.add_dependency(%q<rdoc>, ["~>
|
65
|
-
s.add_dependency(%q<bundler>, ["
|
66
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8
|
67
|
-
s.add_dependency(%q<simplecov>, ["
|
66
|
+
s.add_dependency(%q<rdoc>, ["~> 4.0"])
|
67
|
+
s.add_dependency(%q<bundler>, ["~> 1.2"])
|
68
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8"])
|
69
|
+
s.add_dependency(%q<simplecov>, ["~> 0.7"])
|
68
70
|
end
|
69
71
|
else
|
70
|
-
s.add_dependency(%q<rspec>, ["~> 2.
|
72
|
+
s.add_dependency(%q<rspec>, ["~> 2.13"])
|
71
73
|
s.add_dependency(%q<yard>, ["~> 0.7"])
|
72
|
-
s.add_dependency(%q<rdoc>, ["~>
|
73
|
-
s.add_dependency(%q<bundler>, ["
|
74
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8
|
75
|
-
s.add_dependency(%q<simplecov>, ["
|
74
|
+
s.add_dependency(%q<rdoc>, ["~> 4.0"])
|
75
|
+
s.add_dependency(%q<bundler>, ["~> 1.2"])
|
76
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8"])
|
77
|
+
s.add_dependency(%q<simplecov>, ["~> 0.7"])
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|