def repeat_formula(*args)       #:nodoc:
    # Check for a cell reference in A1 notation and substitute row and column
    args = row_col_notation(args)

    return -1 if args.size < 2   # Check the number of args

    row, col, formula, format, *pairs = args

    # Check that row and col are valid and store max and min values
    return -2 unless check_dimensions(row, col) == 0

    # Enforce an even number of arguments in the pattern/replacement list
    raise "Odd number of elements in pattern/replacement list" unless pairs.size % 2 == 0

    # Check that formula is an array ref
    raise "Not a valid formula" unless formula.respond_to?(:to_ary)

    tokens  = formula.join("\t").split("\t")

    # Ensure that there are tokens to substitute
    raise "No tokens in formula" if tokens.empty?

    # As a temporary and undocumented measure we allow the user to specify the
    # result of the formula by appending a result => value pair to the end
    # of the arguments.
    value = nil
    if pairs[-2] == 'result'
      value = pairs.pop
      pairs.pop
    end

    while !pairs.empty?
      pattern = pairs.shift
      replace = pairs.shift

      tokens.each do |token|
        break if token.sub!(pattern, replace)
      end
    end

    # Change the parameters in the formula cached by the Formula.pm object
    formula  = parser.parse_tokens(tokens)

    raise "Unrecognised token in formula" unless formula

    xf = xf_record_index(row, col, format) # The cell format

    store_formula_common(row, col, xf, value, formula)
    0
  end
def repeat_formula(*args)       #:nodoc:
    # Check for a cell reference in A1 notation and substitute row and column
    args = row_col_notation(args)

    return -1 if args.size < 2   # Check the number of args

    row, col, formula, format, *pairs = args

    # Check that row and col are valid and store max and min values
    return -2 unless check_dimensions(row, col) == 0

    # Enforce an even number of arguments in the pattern/replacement list
    raise "Odd number of elements in pattern/replacement list" unless pairs.size % 2 == 0

    # Check that formula is an array ref
    raise "Not a valid formula" unless formula.respond_to?(:to_ary)

    tokens  = formula.join("\t").split("\t")

    # Ensure that there are tokens to substitute
    raise "No tokens in formula" if tokens.empty?

    # As a temporary and undocumented measure we allow the user to specify the
    # result of the formula by appending a result => value pair to the end
    # of the arguments.
    value = nil
    if pairs[-2] == 'result'
      value = pairs.pop
      pairs.pop
    end

    while !pairs.empty?
      pattern = pairs.shift
      replace = pairs.shift

      tokens.each do |token|
        break if token.sub!(pattern, replace)
      end
    end

    # Change the parameters in the formula cached by the Formula.pm object
    formula  = parser.parse_tokens(tokens)

    raise "Unrecognised token in formula" unless formula

    xf = xf_record_index(row, col, format) # The cell format

    store_formula_common(row, col, xf, value, formula)
    0
  end
:call-seq:
    repeat_formula(row, col,    formula, format, pat, rep, (pat2, rep2,, ...) -> Fixnum
    repeat_formula(A1_notation, formula, format, pat, rep, (pat2, rep2,, ...) -> Fixnum

 Write a formula to the specified row and column (zero indexed) by
 substituting _pattern_ _replacement_ pairs in the formula created via
 store_formula(). This allows the user to repetitively rewrite a formula
 without the significant overhead of parsing.

 Returns  0 : normal termination
         -1 : insufficient number of arguments
         -2 : row or column out of range

 The repeat_formula() method is used in conjunction with store_formula() to
 speed up the generation of repeated formulas. See
 "Improving performance when working with formulas" in
 "FORMULAS AND FUNCTIONS IN EXCEL".

 In many respects repeat_formula() behaves like write_formula() except that
 it is significantly faster.

 The repeat_formula() method creates a new formula based on the pre-parsed
 tokens returned by store_formula(). The new formula is generated by
 substituting _pattern_, _replace_ pairs in the stored formula:

     formula = worksheet.store_formula('=A1 * 3 + 50')

     (0...100).each do |row|
       worksheet.repeat_formula(row, 1, formula, format, 'A1', "A#{row + 1}")
     end

 It should be noted that repeat_formula() doesn't modify the tokens. In the
 above example the substitution is always made against the original token,
 A1, which doesn't change.

 As usual, you can use undef if you don't wish to specify a format:

     worksheet.repeat_formula('B2', formula, format, 'A1', 'A2')
     worksheet.repeat_formula('B3', formula, nil,    'A1', 'A3')

 The substitutions are made from left to right and you can use as many
 pattern, replace pairs as you need. However, each substitution is made
 only once:

     formula = worksheet.store_formula('=A1 + A1')

     # Gives '=B1 + A1'
     worksheet.repeat_formula('B1', formula, undef, 'A1', 'B1')

     # Gives '=B1 + B1'
     worksheet.repeat_formula('B2', formula, undef, 'A1', 'B1', 'A1', 'B1')

 Since the pattern is interpolated each time that it is used it is worth
 using the %q operator to quote the pattern. The qr operator is explained
 in the perlop man page.

     worksheet.repeat_formula('B1', formula, format, %q!A1!, 'A2')

 Care should be taken with the values that are substituted. The formula
 returned by repeat_formula() contains several other tokens in addition to
 those in the formula and these might also match the pattern that you are
 trying to replace. In particular you should avoid substituting a single
 0, 1, 2 or 3.

 You should also be careful to avoid false matches. For example the following
 snippet is meant to change the stored formula in steps
 from =A1 + SIN(A1) to =A10 + SIN(A10).

     formula = worksheet.store_formula('=A1 + SIN(A1)')

     (1..10).each do |row|
       worksheet.repeat_formula(row -1, 1, formula, nil,
                                     'A1', "A#{row}",   #! Bad.
                                     'A1', "A#{row}"    #! Bad.
                               )
     end

 However it contains a bug. In the last iteration of the loop when row is
 10 the following substitutions will occur:

     sub('A1', 'A10')    changes    =A1 + SIN(A1)     to    =A10 + SIN(A1)
     sub('A1', 'A10')    changes    =A10 + SIN(A1)    to    =A100 + SIN(A1) # !!

 The solution in this case is to use a more explicit match such as /(?!A1\d+)A1/:

         worksheet.repeat_formula(row -1, 1, formula, nil,
                                     'A1', 'A' . row,
                                     /(?!A1\d+)A1/, 'A' . row
                                   )

 See also the repeat.rb program in the examples directory of the distro.