# Added by John O. Woods, SciRuby project.
# Derived from GSL-1.9 source files in the specfunc/ dir.

module Distribution
  module MathExtension

    # Error function from GSL-1.9, with epsilon information. Access it using Math.erfc_e
    module Erfc
      P = [ 2.97886562639399288862,
            7.409740605964741794425,
            6.1602098531096305440906,
            5.019049726784267463450058,
            1.275366644729965952479585264,
            0.5641895835477550741253201704 ]
      Q = [ 3.3690752069827527677,
            9.608965327192787870698,
            17.08144074746600431571095,
            12.0489519278551290360340491,
            9.396034016235054150430579648,
            2.260528520767326969591866945,
            1.0 ]

      class << self
        # Estimates erfc(x) valid for 8 < x < 100
        # erfc8_sum from GSL-1.9
        def erfc8_sum(x)
          num = P[5]
          4.downto(0) { |i| num = x*num + P[i] }
          den = Q[6]
          5.downto(0) { |i| den = x*den + Q[i] }
          num / den
        end

        def erfc8(x)
          erfc8_sum(x) * Math.exp(-x*x)
        end

        # gsl_sf_erfc_e
        def evaluate(x, with_error = false)
          ax = x.abs
          e = nil

          if ax <= 1.0
            t = 2*ax-1
            e = ChebyshevSeries.evaluate(:erfc_xlt1, t, with_error)
          elsif ax <= 5.0
            ex2 = Math.exp(-x*x)
            t   = (ax-3).quo(2)
            e   = ChebyshevSeries.evaluate(:erfc_x15, t, with_error)
            if with_error
              e[0] *= ex2
              e[1] = ex2 * (e[1] + 2*x.abs*Float::EPSILON)
            else
              e *= ex2
            end
          elsif ax < 10.0
            exterm = Math.exp(-x*x) / ax
            t      = (2*ax-15).quo(5)
            e = ChebyshevSeries.evaluate(:erfc_x510, t, with_error)
            if with_error
              e[0] *= exterm
              e[1] = exterm * (e[1] + 2*x.abs*Float::EPSILON + Float::EPSILON)
            else
              e *= exterm
            end
          else
            e8 = erfc8(ax)
            e = with_error ? [e8, (x*x + 1) * Float::EPSILON * e8.abs] : e8
          end

          result = x < 0 ? 2 - (with_error ? e.first : e) : (with_error ? e.first : e)
          with_error ? [result, e.last + 2*Float::EPSILON*result.abs] : result
        end
      end

    end

  end
end