module LinearSolver

Constants

VERSION

Public Class Methods

solve_equal(goal, starting_value, &block) click to toggle source
# File lib/linear_solver.rb, line 6
def self.solve_equal(goal, starting_value, &block)
  self.solve_with_stopper(goal, starting_value, ->(a, b) { a == b }, &block)
end
solve_with_stopper(goal, starting_value, stopping_comparator, &block) click to toggle source
# File lib/linear_solver.rb, line 10
def self.solve_with_stopper(goal, starting_value, stopping_comparator, &block)
  raise StandardError unless block_given?

  return 0.0 if stopping_comparator.call(block.call(0.0), goal)

  starting_value = 1 if starting_value == 0
  starting_value = starting_value.to_f
  goal = goal.to_f

  block.call(0) < goal ? input = starting_value.abs : input = -(starting_value.abs)

  upper_bounds_counter = 0
  if input < 0
    while block.call(input) > goal && upper_bounds_counter < 10000
      input *= 2
      upper_bounds_counter += 1
    end
  else
    while block.call(input) < goal && upper_bounds_counter < 10000
      input *= 2
      upper_bounds_counter += 1
    end
  end
  raise CalculationUnsolvableError.new("Upper bound reached, goal: #{goal}, starting value: #{starting_value}, input: #{input}") if upper_bounds_counter >= 10000

  output = block.call(input)
  unless stopping_comparator.call(output, goal)
    difference = input.abs
    asymptote_counter = 0
    begin
      difference /= 2
      input += difference if output < goal
      input -= difference if output > goal
      asymptote_counter += 1
      output = block.call(input)
    end until (stopping_comparator.call(output, goal) || asymptote_counter == 10000)
    raise CalculationUnsolvableError.new("Lower bound asymptote reached, goal: #{goal}, starting value: #{starting_value}, input: #{input}") if asymptote_counter == 10000
  end
  return input
end