# Ruby Calculator - Interactive calculator built with Ruby on Rails
# Author: RSK World (Molla Samser, Founder | Rima Khatun, Designer & Tester)
# Contact: help@rskworld.in | +91 93305 39277
# Website: https://rskworld.in
# Year: 2026
# Description: Build a fully functional calculator using Ruby on Rails with modern UI

class GraphCalculator
  def self.plot_function(equation, x_range = -10..10, points = 100)
    data_points = []
    step = (x_range.end - x_range.begin).to_f / points
    
    (0..points).each do |i|
      x = x_range.begin + (i * step)
      begin
        y = evaluate_equation(equation, x)
        data_points << { x: x.round(4), y: y.round(4) } if y.finite?
      rescue
        # Skip invalid points
      end
    end
    
    {
      equation: equation,
      data_points: data_points,
      x_range: x_range,
      points: points
    }
  end
  
  def self.evaluate_equation(equation, x)
    # Replace common mathematical functions and constants
    processed_eq = equation.downcase
      .gsub(/sin/, 'Math.sin')
      .gsub(/cos/, 'Math.cos')
      .gsub(/tan/, 'Math.tan')
      .gsub(/log/, 'Math.log10')
      .gsub(/ln/, 'Math.log')
      .gsub(/sqrt/, 'Math.sqrt')
      .gsub(/abs/, 'Math.abs')
      .gsub(/pi/, 'Math::PI')
      .gsub(/e/, 'Math::E')
      .gsub(/\^/, '**')
      .gsub(/x/, x.to_s)
    
    eval(processed_eq)
  end
  
  def self.find_roots(equation, range = -100..100, tolerance = 0.0001)
    roots = []
    step = 0.1
    prev_y = nil
    
    (range.begin).step(range.end, step).each do |x|
      begin
        y = evaluate_equation(equation, x)
        
        if prev_y && (prev_y * y < 0) # Sign change detected
          # Use bisection method to refine root
          root = bisection_method(equation, x - step, x, tolerance)
          roots << root.round(6) if root
        end
        
        prev_y = y
      rescue
        # Skip invalid points
      end
    end
    
    roots.uniq
  end
  
  def self.bisection_method(equation, a, b, tolerance)
    return nil if evaluate_equation(equation, a) * evaluate_equation(equation, b) > 0
    
    while (b - a).abs > tolerance
      c = (a + b) / 2
      fc = evaluate_equation(equation, c)
      
      if fc.abs < tolerance
        return c
      elsif evaluate_equation(equation, a) * fc < 0
        b = c
      else
        a = c
      end
    end
    
    (a + b) / 2
  end
  
  def self.calculate_derivative(equation, x, h = 0.0001)
    begin
      f1 = evaluate_equation(equation, x + h)
      f2 = evaluate_equation(equation, x - h)
      (f1 - f2) / (2 * h)
    rescue
      0
    end
  end
  
  def self.calculate_integral(equation, a, b, n = 1000)
    # Simpson's rule for numerical integration
    h = (b - a).to_f / n
    sum = evaluate_equation(equation, a) + evaluate_equation(equation, b)
    
    (1..n-1).step(2).each do |i|
      x = a + i * h
      sum += 4 * evaluate_equation(equation, x)
    end
    
    (2..n-2).step(2).each do |i|
      x = a + i * h
      sum += 2 * evaluate_equation(equation, x)
    end
    
    (h / 3) * sum
  end
  
  def self.generate_chart_data(equations, x_range = -10..10)
    datasets = equations.map.with_index do |eq, index|
      data = plot_function(eq, x_range)
      {
        label: eq,
        data: data[:data_points].map { |point| [point[:x], point[:y]] },
        borderColor: get_color(index),
        backgroundColor: get_color(index, 0.1),
        fill: false,
        tension: 0.1
      }
    end
    
    {
      labels: plot_function(equations.first, x_range)[:data_points].map { |p| p[:x] },
      datasets: datasets
    }
  end
  
  private
  
  def self.get_color(index, alpha = 1)
    colors = [
      "rgba(255, 99, 132, #{alpha})",
      "rgba(54, 162, 235, #{alpha})",
      "rgba(255, 206, 86, #{alpha})",
      "rgba(75, 192, 192, #{alpha})",
      "rgba(153, 102, 255, #{alpha})",
      "rgba(255, 159, 64, #{alpha})"
    ]
    colors[index % colors.length]
  end
end
