require 'facets/math/linsolve'
module Math
# Returns array of real solution of ax**2 + bx + c = d
# or nil
if no or an infinite number of solutions exist.
# If +d+ is missing it is assumed to be 0.
#
# In order to solve ax**2 + bx + c = d
+sqsolve+ identifies several cases:
# * a == 0:
# The equation to be solved is the linear equation bx + c = d
. #sqsolve> delegates the computation to
# #linsolve>. If it results in +nil+, +nil+ is returned (not [nil]
!). Otherwise a one-element array
# containing result of #linsolve is returned.
# * a != 0:
# The equation to be solved actually is a second order one.
# * c == d
# The equation to be solved is ax**2 + bx = 0
. One solution of this equation obviously is
# x = 0
, the second one solves ax + b = 0
. The solution of the latter is
# delegated to +linsolve+. An array containing both results in ascending order is returned.
# * c != d
# The equation cannot be separated into x
times some factor.
# * b == 0
# The equation to be solved is ax**2 + c = d
. This can be written as the linear equation
# ay + c = d
with y = x ** 2
. The solution of the linear equation is delegated
# to +linsolve+. If the returned value for +y+ is +nil+, that becomes the overall return value.
# Otherwise an array containing the negative and positive squareroot of +y+ is returned
# * b != 0
# The equation cannot be reduced to simpler cases. We now first have to compute what is called the
# discriminant x = b**2 + 4a(d - c)
(that's what we need to compute the square root of).
# If the descriminant is negative no real solution exists and nil
is returned. The ternary
# operator checking whether b
is negative does ensure better numerical stability --only one
# of the two solutions is computed using the widely know formula for solving second order equations.
# The second one is computed from the fact that the product of both solutions is (c - d) / a
.
# Take a look at a book on numerical mathematics if you don't understand why this should be done.
#
# @author Josef Schugt
def self.sqsolve(a, b, c, d = 0.0)
if a == 0.0
x = linsolve(b, c, d)
return x.nil? ? nil: [ linsolve(b, c, d) ]
else
return [0.0, linsolve(a, b)].sort if c == d
if b == 0.0
x = linsolve(a, c, d)
x < 0.0 ? nil : [-Math.sqrt(x), Math.sqrt(x)]
else
x = b * b + 4.0 * a * (d - c)
return nil if x < 0.0
x = b < 0 ? b - Math.sqrt(x) : b + Math.sqrt(x)
[-0.5 * x / a, 2.0 * (d - c) / x].sort
end
end
end
end