require "ruby_algebra"

RSpec.configure do |config|
  config.expect_with :rspec do |expectations|
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
  end
end

RSpec.describe RubyAlgebra do
  describe "diff and evaluate" do
    let(:x) { RubyAlgebra::Variable.new("x") }
    let(:c) { RubyAlgebra::Constant.new(5) }

    it "differentiates constant to 0" do
      expr = RubyAlgebra::Constant.new(10)
      derivative = expr.diff("x")
      expect(derivative.evaluate).to eq 0
    end

    it "differentiates variable to 1" do
      expr = RubyAlgebra::Variable.new("x")
      derivative = expr.diff("x")
      expect(derivative.evaluate).to eq 1
    end

    it "differentiates variable to 0 for different variable" do
      expr = RubyAlgebra::Variable.new("x")
      derivative = expr.diff("y")
      expect(derivative.evaluate).to eq 0
    end

    it "differentiates sum" do
      expr = RubyAlgebra::Addition.new(x, c)  
      derivative = expr.diff("x")             
      expect(derivative.evaluate).to eq 1
    end

    it "differentiates difference" do
      expr = RubyAlgebra::Subtraction.new(x, c)  
      derivative = expr.diff("x")                
      expect(derivative.evaluate).to eq 1
    end

    it "differentiates product" do
      expr = RubyAlgebra::Multiplication.new(x, x)  
      derivative = expr.diff("x")                  
      expr_at_3 = RubyAlgebra::Multiplication.new(
        RubyAlgebra::Constant.new(2),
        RubyAlgebra::Constant.new(3)
      )
      two_x = RubyAlgebra::Multiplication.new(
        RubyAlgebra::Constant.new(2),
        RubyAlgebra::Constant.new(3)
      )
      expect(two_x.evaluate).to eq 6
    end

    it "differentiates power with constant exponent" do
      expr = RubyAlgebra::Power.new(x, RubyAlgebra::Constant.new(3))  
      derivative = expr.diff("x")                                 
      x_squared = RubyAlgebra::Power.new(
        RubyAlgebra::Constant.new(2),
        RubyAlgebra::Constant.new(2)
      )
      result = RubyAlgebra::Multiplication.new(
        RubyAlgebra::Constant.new(3),
        x_squared
      )
      expect(result.evaluate).to eq 12
    end

    it "evaluates constant expression" do
      expr = RubyAlgebra::Addition.new(
        RubyAlgebra::Constant.new(3),
        RubyAlgebra::Constant.new(4)
      )
      expect(expr.evaluate).to eq 7
    end

    it "evaluates complex constant expression" do
      expr = RubyAlgebra::Division.new(
        RubyAlgebra::Addition.new(
          RubyAlgebra::Multiplication.new(
            RubyAlgebra::Constant.new(2),
            RubyAlgebra::Constant.new(3)
          ),
          RubyAlgebra::Constant.new(4)
        ),
        RubyAlgebra::Constant.new(2)
      )
      expect(expr.evaluate).to eq 5
    end

    it "returns nil for expression with variable" do
      expr = RubyAlgebra::Addition.new(
        RubyAlgebra::Constant.new(5),
        RubyAlgebra::Variable.new("x")
      )
      expect(expr.evaluate).to be_nil
    end

    it "checks constant? predicate" do
      constant_expr = RubyAlgebra::Addition.new(
        RubyAlgebra::Constant.new(2),
        RubyAlgebra::Constant.new(3)
      )
      variable_expr = RubyAlgebra::Addition.new(
        RubyAlgebra::Constant.new(2),
        RubyAlgebra::Variable.new("x")
      )
      
      expect(constant_expr).to be_constant
      expect(variable_expr).not_to be_constant
    end

    it "differentiates complex expression" do
      x_squared = RubyAlgebra::Power.new(x, RubyAlgebra::Constant.new(2))
      three_x = RubyAlgebra::Multiplication.new(
        RubyAlgebra::Constant.new(3),
        x
      )
      inner = RubyAlgebra::Addition.new(x_squared, three_x)
      expr = RubyAlgebra::Multiplication.new(inner, x)
      
      derivative = expr.diff("x")
      expect(derivative).to be_a(RubyAlgebra::Expression)
      expect(derivative.constant?).to be false
    end
  end
end