magician 0.2.1 → 0.3.0

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