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.
@@ -1,121 +1,172 @@
1
1
  # Magician's extensions to the Math module.
2
2
  module Math
3
3
 
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. Returns nil if a is 0.
10
- #
11
- # @param [Numeric] a the first coefficient (must not be 0)
12
- # @param [Numeric] b the second coefficient
13
- # @param [Numeric] c the third coefficient
14
- #
15
- # @return [Array] a sorted array of two Floats, the two possible values for x
16
- def quadratic(a, b, c)
17
- return nil if a.zero?
18
- left = -b
19
- right = Math.sqrt(b**2 - 4*a*c)
20
- bottom = 2*a
21
- [ (left+right)/bottom, (left-right)/bottom ].sort
22
- end
23
-
24
- # The number of size k ordered subsets of a set of size n. Equivalent to
25
- # n!/(n-k)!. Returns nil if either is negative, or if n < k.
26
- #
27
- # @param [Integer] n the size of the set to pick from
28
- # @param [Integer] k the size of the ordered subsets
29
- #
30
- # @return [Integer] the number of permutations
31
- def permutations(n, k)
32
- return nil if n < 0 or k < 0 or n < k
33
- n.factorial / (n-k).factorial
34
- end
35
-
36
- # The number of size k unordered subsets of a set of size n. Equivalent to
37
- # n!/(k!(n-k)!). Returns nil if either is negative, or if n < k.
38
- #
39
- # @param [Integer] n the size of the set to pick from
40
- # @param [Integer] k the size of the unordered subsets
41
- #
42
- # @return [Integer] the number of combinations
43
- def combinations(n, k)
44
- return nil if n < 0 or k < 0 or n < k
45
- n.factorial / (k.factorial * (n-k).factorial)
46
- end
47
-
48
- # Get the number of steps it takes to get from integer n to 1 using the
49
- # Collatz conjecture (set http://en.wikipedia.org/wiki/Collatz_conjecture).
50
- # Returns nil if n < 1.
51
- #
52
- # @param [Integer] n the number to put into the Collatz conjecture initially
53
- # @param [Integer] depth the number of steps that have passed so far (should
54
- # not be modified unless this is being cached carefully)
55
- #
56
- # @return [Integer] the number of steps it takes to get from integer n to 1
57
- # using the Collatz conjecture (the depth)
58
- def collatz(n, depth=0)
59
- return nil if n < 1
60
- if n == 1
61
- depth
62
- elsif n % 2 == 0
63
- depth += 1
64
- collatz(n/2, depth)
65
- else
66
- depth += 1
67
- collatz(3*n + 1, depth)
68
- end
69
- end
70
-
71
- # Using the Pythagorean theorem, gets c (the length of the hypotenuse) when a
72
- # and b (the lengths of the other sides of a triangle) are given. Returns nil
73
- # if either a or b is negative.
74
- #
75
- # @param [Numeric] a the length of the first side of the triangle
76
- # @param [Numeric] b the length of the second side of the triangle
77
- #
78
- # @return [Float] the length of the hypotenuse of the triangle
79
- def hypotenuse(a, b)
80
- [a,b].each do |n|
81
- return nil if n < 0
82
- end
83
- Math.sqrt(a**2 + b**2)
84
- end
85
-
86
- # Returns true if the three given numbers are positive integers that form a
87
- # Pythagorean triplet (that is, if a^2+b^2=c^2). C must be the last parameter.
88
- #
89
- # @param [Integer] a the length of the first side of the triangle
90
- # @param [Integer] b the length of the second side of the triangle
91
- # @param [Integer] c the length of the hypotenuse of the triangle
92
- #
93
- # @return [Boolean] true if the three numbers form a Pythagorean triplet
94
- def triplet?(a, b, c)
95
- inputs_are_valid = true
96
- [a,b,c].each do |n|
97
- inputs_are_valid = false if n < 1 or not n.class <= Integer
98
- end
99
- return false unless inputs_are_valid
100
- a**2 + b**2 == c**2
101
- end
102
-
103
- # Calculates a series of Fibonacci numbers of a specified length. Returns nil
104
- # if a negative length is given.
105
- #
106
- # @param [Integer] length the length of the Fibonacci series that should be
107
- # returned
108
- #
109
- # @return [Array] a Fibonacci series of Integers with the specified length
110
- # (ordered)
111
- def fibs length
112
- return nil if length < 0
113
- terms = []
114
- until terms.length == length do
115
- at_beginning = [0,1].include? terms.length
116
- terms << ( at_beginning ? 1 : terms[-2] + terms[-1] )
117
- end
118
- terms
119
- end
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
@@ -1,31 +1,43 @@
1
1
  # Magician's extensions to the Numeric class (affects Integers and Floats).
2
2
  class Numeric
3
3
 
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
- return false if n.zero?
13
- (self % n).zero?
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
- # Performs to_s[selection].to_i on the number. Note that for floats, the
17
- # decimal counts as a digit within the string.
18
- # TODO: Let this intelligently convert back to an Integer or Float.
19
- #
20
- # @param [Range] selection the selection/range to get from the number (you can
21
- # use anything that works with the [] syntax)
22
- #
23
- # @return [Integer] substring of the number (using []), converted to an
24
- # Integer
25
- #
26
- # @deprecated because it's useless and wrong
27
- def digits selection
28
- to_s[selection].to_i
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
- end
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
@@ -6,5 +6,8 @@ PI = Math::PI
6
6
  # Alias Math::E to E
7
7
  E = Math::E
8
8
 
9
- # the Golden ratio
10
- GOLDEN_RATIO = (1 + Math.sqrt(5))/2
9
+ # Alias Complex::I to I
10
+ I = Complex::I
11
+
12
+ # The golden ratio
13
+ GOLDEN_RATIO = (1 + Math.sqrt(5))/2
@@ -1,12 +1,12 @@
1
1
  # Magician's extensions to the String class.
2
2
  class String
3
3
 
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
- self == reverse
10
- end
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
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "magician"
8
- s.version = "0.2.1"
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 = "2012-12-10"
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.24"
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.12.0"])
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>, ["~> 3.12"])
58
- s.add_development_dependency(%q<bundler>, [">= 0"])
59
- s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
60
- s.add_development_dependency(%q<simplecov>, [">= 0"])
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.12.0"])
64
+ s.add_dependency(%q<rspec>, ["~> 2.13"])
63
65
  s.add_dependency(%q<yard>, ["~> 0.7"])
64
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
65
- s.add_dependency(%q<bundler>, [">= 0"])
66
- s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
67
- s.add_dependency(%q<simplecov>, [">= 0"])
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.12.0"])
72
+ s.add_dependency(%q<rspec>, ["~> 2.13"])
71
73
  s.add_dependency(%q<yard>, ["~> 0.7"])
72
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
73
- s.add_dependency(%q<bundler>, [">= 0"])
74
- s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
75
- s.add_dependency(%q<simplecov>, [">= 0"])
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