module BigDecimal::Internal

def self.coerce_to_bigdecimal(x, prec, method_name) # :nodoc:

:nodoc:
TODO: some methods (example: BigMath.exp) require more precision than specified to coerce.
Coerce x to BigDecimal with the specified precision.
def self.coerce_to_bigdecimal(x, prec, method_name) # :nodoc:
  case x
  when BigDecimal
    return x
  when Integer, Float
    return BigDecimal(x, 0)
  when Rational
    return BigDecimal(x, [prec, 2 * BigDecimal.double_fig].max)
  end
  raise ArgumentError, "#{x.inspect} can't be coerced into BigDecimal"
end

def self.coerce_validate_prec(prec, method_name, accept_zero: false) # :nodoc:

:nodoc:
def self.coerce_validate_prec(prec, method_name, accept_zero: false) # :nodoc:
  unless Integer === prec
    original = prec
    # Emulate Integer.try_convert for ruby < 3.1
    if prec.respond_to?(:to_int)
      prec = prec.to_int
    else
      raise TypeError, "no implicit conversion of #{original.class} into Integer"
    end
    raise TypeError, "can't convert #{original.class} to Integer" unless Integer === prec
  end
  if accept_zero
    raise ArgumentError, "Negative precision for #{method_name}" if prec < 0
  else
    raise ArgumentError, "Zero or negative precision for #{method_name}" if prec <= 0
  end
  prec
end

def self.fast_to_f(x) # :nodoc:

:nodoc:
TODO: Remove this workaround when BigDecimal#to_f is optimized.
to get the exact nearest float representation.
This is because to_f internally converts BigDecimal to String
Bigdecimal#to_f is slow when n_significant_digits is large.
Fast and rough conversion to float for mathematical calculations.
def self.fast_to_f(x) # :nodoc:
  x.n_significant_digits < 40 ? x.to_f : x.mult(1, 20).to_f
end

def self.float_log(x) # :nodoc:

:nodoc:
Calculates Math.log(x.to_f) considering large or small exponent
def self.float_log(x) # :nodoc:
  Math.log(fast_to_f(x._decimal_shift(-x.exponent))) + x.exponent * Math.log(10)
end

def self.infinity_computation_result # :nodoc:

:nodoc:
def self.infinity_computation_result # :nodoc:
  if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_INFINITY)
    raise FloatDomainError, "Computation results in 'Infinity'"
  end
  BigDecimal::INFINITY
end

def self.nan_computation_result # :nodoc:

:nodoc:
def self.nan_computation_result # :nodoc:
  if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_NaN)
    raise FloatDomainError, "Computation results to 'NaN'"
  end
  BigDecimal::NAN
end

def self.newton_loop(prec, initial_precision: BigDecimal.double_fig / 2, safe_margin: 2) # :nodoc:

:nodoc:
Iteration for Newton's method with increasing precision
def self.newton_loop(prec, initial_precision: BigDecimal.double_fig / 2, safe_margin: 2) # :nodoc:
  precs = []
  while prec > initial_precision
    precs << prec
    prec = (precs.last + 1) / 2 + safe_margin
  end
  precs.reverse_each do |p|
    yield p
  end
end

def self.taylor_sum_binary_splitting(x, ds, prec) # :nodoc:

:nodoc:
x.n_significant_digits or ds.size must be small to be performant.
Calculates f(x) = (x/d0)*(1+(x/d1)*(1+(x/d2)*(1+(x/d3)*(1+...))))
Calculating Taylor series sum using binary splitting method
def self.taylor_sum_binary_splitting(x, ds, prec) # :nodoc:
  fs = ds.map {|d| [0, BigDecimal(d)] }
  # fs = [[a0, a1], [b0, b1], [c0, c1], ...]
  # f(x) = a0/a1+(x/a1)*(1+b0/b1+(x/b1)*(1+c0/c1+(x/c1)*(1+d0/d1+(x/d1)*(1+...))))
  while fs.size > 1
    # Merge two adjacent fractions
    # from: (1 + a0/a1 + x/a1 * (1 + b0/b1 + x/b1 * rest))
    # to:   (1 + (a0*b1+x*(b0+b1))/(a1*b1) + (x*x)/(a1*b1) * rest)
    xn = xn ? xn.mult(xn, prec) : x
    fs = fs.each_slice(2).map do |(a, b)|
      b ||= [0, BigDecimal(1)._decimal_shift([xn.exponent, 0].max + 2)]
      [
        (a[0] * b[1]).add(xn * (b[0] + b[1]), prec),
        a[1].mult(b[1], prec)
      ]
    end
  end
  BigDecimal(fs[0][0]).div(fs[0][1], prec)
end