Class: Factoradic

Inherits:
Object
  • Object
show all
Defined in:
lib/factoradic.rb,
lib/factoradic/version.rb

Constant Summary collapse

DEFAULT_OPTIONS =
{
  # disabling this will save ram when computing
  # very large (Bignum) factorial values
  memoize_factorial_values: false,

  # the separator used between placex in factorial base
  separator: ':'
}
VERSION =
'1.0.0'

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeFactoradic

Returns a new instance of Factoradic.



112
113
114
115
# File 'lib/factoradic.rb', line 112

def initialize
  @value = 0
  @digits = [0]
end

Class Attribute Details

.optionsObject

Returns the value of attribute options.



21
22
23
# File 'lib/factoradic.rb', line 21

def options
  @options
end

Class Method Details

.autoconvert(str) ⇒ Object



65
66
67
68
69
70
71
# File 'lib/factoradic.rb', line 65

def autoconvert(str)
  if string_is_factoradic?(str)
    convert_factoradic_to_decimal(str)
  else
    convert_decimal_to_factoradic(str)
  end
end

.basic_factorial(n) ⇒ Object



99
100
101
# File 'lib/factoradic.rb', line 99

def basic_factorial(n)
  (1..n).reduce(1, :*)
end

.convert_decimal_to_factoradic(decimal) ⇒ Object Also known as: d2f



79
80
81
82
# File 'lib/factoradic.rb', line 79

def convert_decimal_to_factoradic(decimal)
  f = parse_decimal(decimal)
  f.to_s
end

.convert_factoradic_to_decimal(str) ⇒ Object Also known as: f2d



73
74
75
76
# File 'lib/factoradic.rb', line 73

def convert_factoradic_to_decimal(str)
  f = parse_factoradic(str)
  f.to_i.to_s(10)
end

.factorial(n) ⇒ Object



103
104
105
106
107
108
109
# File 'lib/factoradic.rb', line 103

def factorial(n)
  if options.memoize_factorial_values
    memoized_factorial(n)
  else
    basic_factorial(n)
  end
end

.memoized_factorial(n) ⇒ Object



91
92
93
94
95
96
97
# File 'lib/factoradic.rb', line 91

def memoized_factorial(n)
  if @factorial_sequence.length > n
    @factorial_sequence[n]
  else
    @factorial_sequence[n] = n * memoized_factorial(n - 1)
  end
end

.nonstandard_separator?Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/factoradic.rb', line 23

def nonstandard_separator?
  options.separator != DEFAULT_OPTIONS[:separator]
end

.parse(str) ⇒ Object



57
58
59
60
61
62
63
# File 'lib/factoradic.rb', line 57

def parse(str)
  if string_is_factoradic?(str)
    [parse_factoradic(str), :factorial]
  else
    [parse_decimal(str), :decimal]
  end
end

.parse_decimal(decimal) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/factoradic.rb', line 42

def parse_decimal(decimal)
  intvalue = case decimal
             when Integer
               decimal
             when String
               decimal.to_i(10)
             else
               raise ArgumentError, "Expected an Integer or String{"
             end

  new.tap do |f|
    f.value = intvalue
  end
end

.parse_factoradic(str) ⇒ Object



36
37
38
39
40
# File 'lib/factoradic.rb', line 36

def parse_factoradic(str)
  new.tap do |f|
    f.parse_factoradic(str)
  end
end

.string_is_factoradic?(str) ⇒ Boolean

Returns:

  • (Boolean)


27
28
29
30
31
32
33
34
# File 'lib/factoradic.rb', line 27

def string_is_factoradic?(str)
  re = if nonstandard_separator?
         /\A\d+([#{options.separator}]\d+)+\Z/
       else
         /\A\d+([,:]\d+)+\Z/
       end
  !!(str =~ re)
end

.valid_factoradic_digits?(digit_list) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
88
89
# File 'lib/factoradic.rb', line 85

def valid_factoradic_digits?(digit_list)
  digit_list.reverse.map.with_index do |digit, idx|
    (digit >= 0) && (digit <= idx)
  end.all?(true)
end

Instance Method Details

#[](i) ⇒ Object



141
142
143
# File 'lib/factoradic.rb', line 141

def [](i)
  @digits[i]
end

#[]=(i, rvalue) ⇒ Object



145
146
147
148
149
# File 'lib/factoradic.rb', line 145

def []=(i, rvalue)
  new_places = @digits.dup
  new_places[i] = rvalue.to_i
  self.digits = new_digits
end

#digits=(new_digits) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/factoradic.rb', line 161

def digits=(new_digits)
  new_digits = new_digits.map do |x|
    if x.nil?
      0
    else
      x
    end
  end

  unless Factoradic.valid_factoradic_digits?(new_digits)
    raise ArgumentError, "Invalid factoradic digits: #{new_digits.inspect}"
  end

  @digits = new_digits
  recompute_value!
end

#inspectObject



191
192
193
# File 'lib/factoradic.rb', line 191

def inspect
  "#<Factoradic #{to_s}>"
end

#optionsObject



117
118
119
# File 'lib/factoradic.rb', line 117

def options
  self.class.options
end

#parse_factoradic(str) ⇒ Object



151
152
153
154
155
156
157
158
159
# File 'lib/factoradic.rb', line 151

def parse_factoradic(str)
  sep = if Factoradic.nonstandard_separator?
          /[#{Factoradic.options.separator}]/
        else
          /[,:]/
        end

  self.digits = str.split(sep).map{ |x| x.to_i }
end

#recompute_digits!Object



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/factoradic.rb', line 121

def recompute_digits!
  x = @value
  divisor = 2
  @digits = [0]

  while x > 0
    x, r = x.divmod(divisor)
    @digits.push(r)
    divisor += 1
  end

  @digits.reverse!
end

#recompute_value!Object



135
136
137
138
139
# File 'lib/factoradic.rb', line 135

def recompute_value!
  @value = @digits.reverse.map.with_index do |digit, idx|
    digit * Factoradic.factorial(idx)
  end.reduce(&:+)
end

#to_iObject



187
188
189
# File 'lib/factoradic.rb', line 187

def to_i
  @value
end

#to_s(separator = options.separator) ⇒ Object



183
184
185
# File 'lib/factoradic.rb', line 183

def to_s(separator = options.separator)
  @digits.join(separator)
end

#value=(new_value) ⇒ Object



178
179
180
181
# File 'lib/factoradic.rb', line 178

def value=(new_value)
  @value = new_value.to_i
  recompute_digits!
end