```#! /usr/bin/env ruby
#
#  quiz-128  --  Ruby Quiz #128  --  Verbal Arithmetic.
#
#  Usage:  quiz-128 [ -v ] '<equation string>'
#
#  (http://www.rubyquiz.com/quiz128.html).
#
#  Glen Pankow      06/17/07        Original version.
#
#
#-----------------------------------------------------------------------------
#
#  I take a slightly different approach to this quiz:  I build up code that
#  kinda-sorta models addition as taught in U.S. elementary schools (taking
#  into account the various constraints of the problem), then eval it.
#
#  And instead of generating permutations of unassigned digits and using
#  recursion for backtracking, I have a single array of digits that is used
#  sort of as a mask (nil entries mark assigned digits) and use Ruby's
#  wonderful magic iterator facility to scan through it.
#
#  \$ uname -srmpio
#  Linux 2.6.9-55.ELsmp i686 i686 i386 GNU/Linux
#
#  \$ time ./quiz-128 'send + more = money'
#  d = 7, e = 5, y = 2, n = 6, r = 8, o = 0, s = 9, m = 1
#      1   0   1   1
#        s:9 e:5 n:6 d:7
#  +     m:1 o:0 r:8 e:5
#  ---------------------
#    m:1 o:0 n:6 e:5 y:2
#  0.065u 0.003s 0:00.07 85.7%     0+0k 0+0io 0pf+0w
#
#  \$ time ./quiz-128 forty ten ten sixty
#  y = 6, n = 0, t = 8, e = 5, r = 7, x = 4, o = 9, i = 1, f = 2, s = 3
#      1   2   1   0
#    f:2 o:9 r:7 t:8 y:6
#            t:8 e:5 n:0
#  +         t:8 e:5 n:0
#  ---------------------
#    s:3 i:1 x:4 t:8 y:6
#  0.029u 0.002s 0:00.03 66.6%     0+0k 0+0io 0pf+0w
#
#  \$ time ./quiz-128 eat+that=apple
#  t = 9, e = 8, a = 1, l = 3, h = 2, p = 0
#      1   1   0   1
#            e:8 a:1 t:9
#  +     t:9 h:2 a:1 t:9
#  ---------------------
#    a:1 p:0 p:0 l:3 e:8
#  0.008u 0.002s 0:00.01 0.0%      0+0k 0+0io 0pf+0w
#
#  \$ time ./quiz-128 ruby rubber baby buggy bumper
#  y = 0, r = 7, b = 8, e = 1, g = 4, u = 2, a = 3, p = 9, m = 6
#  ...
#  y = 0, r = 7, b = 8, e = 5, g = 4, u = 2, a = 3, p = 9, m = 6
#      1   2   1   2   0
#            r:7 u:2 b:8 y:0
#    r:7 u:2 b:8 b:8 e:5 r:7
#            b:8 a:3 b:8 y:0
#  +     b:8 u:2 g:4 g:4 y:0
#  -------------------------
#    b:8 u:2 m:6 p:9 e:5 r:7
#  0.120u 0.002s 0:00.13 92.3%     0+0k 0+0io 0pf+0w
#

class Array

#
# For each non-nil element of the current array, (destructively) set it to
# nil (i.e., 'marking' it), yield the original value (to the assumed block),
# and restore it back to what it originally was (i.e., 'unmarking' it).
#
# For example, the code:
#    array = [ 0, nil, 2, 3 ]
#    p "before: array = #{array.inspect}"
#    array.unmarkeds { |elem| p "elem = #{elem}, array = #{array.inspect}" }
#    p "after: array = #{array.inspect}"
# would print:
#    before: array = [0, nil, 2, 3]
#    elem = 0, array = [nil, nil, 2, 3]
#    elem = 2, array = [0, nil, nil, 3]
#    elem = 3, array = [0, nil, 2, nil]
#    after: array = [0, nil, 2, 3]
#
def unmarkeds
(0...size).each do |i|
next if (at(i).nil?)
elem = at(i)  ;  self[i] = nil
yield elem
self[i] = elem
end
end

#
# If the <i>-th element of the current array is nil, do nothing.  Otherwise
# (destructively) set it to nil (i.e., 'marking' it), yield (to the assumed
# block), and restore it back to what it originally was (i.e., 'unmarking'
# it).  This is basically the guts of unmarkeds(), and is provided for safe
# manual element marking.
#
def if_unmarked(i)
return if (at(i).nil?)
elem = at(i)  ;  self[i] = nil
yield
self[i] = elem
end

#
# Note:  typically one might say 'yield elem, self' in these methods, but
# I don't need them for this application due to Ruby's scoping mechanism.
#
end

#
# Process the command-line arguments of addend strings and the sum string.
#
verbose = false
ARGV.each do |arg|
if (arg == '-v')
verbose = true
else
arg.split(/[\s+=]+/).each { |term| addend_strs << term }
end
end

#
# Split the strings up into their component letters; create some (ugly) code to
# eventually print out a nice table of the addition.
#
table_print_code = 'print " '
(sum_str.length - 1).downto(1) do |i|
table_print_code << "   \#{carry#{i}}"
end
table_print_code << "\\n\"\n"
first_letters = { }
table_print_code \
<< 'print "' << ((i < addend_strs.size - 1)? ' ' : '+') \
<< '    ' * (sum_str.length - addend_str.length) \
<< addend_str.gsub(/([a-z])/, ' \1:#{\1}') << "\\n\"\n"
end
table_print_code \
<< 'print "-' << ('----' * sum_str.length) << "\\n\"\n" \
<< 'print " ' << sum_str.gsub(/([a-z])/, ' \1:#{\1}') << "\\n\"\n"
sum_chars = sum_str.split(//).reverse
first_letters[sum_chars[-1]] = 1

#
#
# This, too, is quite ugly and I don't bother to document it, as printing out
# the generated code will probably give one a better idea of how it works than
# my usual verbose documentation (i.e., run this script with -v).
#
seen_chars = { }
code_tail = ''
indent = ''
(0...sum_chars.size).each do |col|
sum_char = sum_chars[col]
col_sum_code = "#{indent}carry#{col+1}, #{sum_char} = (carry#{col}"
next if (dig_char.nil?)
if (seen_chars[dig_char].nil?)
code_tail[0,0] = "#{indent}end\n"
indent << '   '
seen_chars[dig_char] = 1
if (first_letters.has_key?(dig_char))
col_sum_code[0,0] = '   '   # fix indentation
answer_print_code << ", #{dig_char} = \#{#{dig_char}}"
end
col_sum_code << " + #{dig_char}"
end
col_sum_code << ").divmod(10)\n"
col_sum_code.sub!(/\(carry0 \+ /, '(')
if (seen_chars[sum_char].nil?)
if (first_letters.has_key?(sum_char))
code_tail[0,0] = "#{indent}end\n"
indent << '   '
seen_chars[sum_char] = 1
answer_print_code << ", #{sum_char} = \#{#{sum_char}}"
else
col_sum_code.sub!(/ = /, '2 = ')
<< col_sum_code \
<< "#{indent}next unless (#{sum_char}2 == #{sum_char})  # inconsistent?\n"
end
end