Тодор обнови решението на 14.11.2012 14:41 (преди почти 13 години)
+class UndefinedVariableError < StandardError
+end
+
+class Expr
+  attr_accessor :s_expr
+
+  def initialize(s_expr)
+    @s_expr = s_expr
+  end
+
+  def self.build(s_expr)
+    new s_expr
+  end
+
+  def ==(other_expr)
+    s_expr == other_expr.s_expr
+  end
+
+  def +(other_expr)
+    Expr.build [:+, s_expr, other_expr.s_expr]
+  end
+
+  def *(other_expr)
+    Expr.build [:*, s_expr, other_expr.s_expr]
+  end
+
+  def -@
+    Expr.build [:-, s_expr]
+  end
+
+  def evaluate(environment = {})
+    case s_expr[0]
+      when :number then return Number.build(s_expr).evaluate(environment)
+      when :variable then return Variable.build(s_expr).evaluate(environment)
+      when :+ then return Addition.build(s_expr).evaluate(environment)
+      when :* then return Multiplication.build(s_expr).evaluate(environment)
+      when :- then return Negation.build(s_expr).evaluate(environment)
+      when :sin then return Sine.build(s_expr).evaluate(environment)
+      when :cos then return Cosine.build(s_expr).evaluate(environment)
+    end
+  end
+
+  def derive(variable)
+    case s_expr[0]
+      when :number then return Number.build(s_expr).derive(variable).simplify
+      when :variable then return Variable.build(s_expr).derive(variable).simplify
+      when :+ then return Addition.build(s_expr).derive(variable).simplify
+      when :* then return Multiplication.build(s_expr).derive(variable).simplify
+      when :- then return Negation.build(s_expr).derive(variable).simplify
+      when :sin then return Sine.build(s_expr).derive(variable).simplify
+      when :cos then return Cosine.build(s_expr).derive(variable).simplify
+    end
+  end
+
+  def simplify
+    case s_expr[0]
+      when :number then return Number.build(s_expr).simplify
+      when :variable then return Variable.build(s_expr).simplify
+      when :+ then return Addition.build(s_expr).simplify
+      when :* then return Multiplication.build(s_expr).simplify
+      when :- then return Negation.build(s_expr).simplify
+      when :sin then return Sine.build(s_expr).simplify
+      when :cos then return Cosine.build(s_expr).simplify
+    end
+  end
+
+end
+
+class Unary < Expr
+  def operand
+    s_expr[1]
+  end
+end
+
+class Binary < Expr
+  def operand1
+    s_expr[1]
+  end
+
+  def operand2
+    s_expr[2]
+  end
+end
+
+class Number < Unary
+  def evaluate(environment = {})
+    operand
+  end
+
+  def simplify
+    self
+  end
+
+  def derive(variable)
+    Expr.build [:number, 0]
+  end
+end
+
+class Variable < Unary
+  def evaluate(environment = {})
+    if environment.has_key? operand
+      environment[operand]
+    else
+      raise UndefinedVariableError
+    end
+  end
+
+  def simplify
+    self
+  end
+
+  def derive(variable)
+    if operand == variable
+      return Expr.build [:number, 1]
+    else
+      return Expr.build [:number, 0]
+    end
+  end
+end
+
+class Addition < Binary
+  def evaluate(environment = {})
+    Expr.build(operand1).evaluate(environment) + Expr.build(operand2).evaluate(environment)
+  end
+
+  def simplify
+    begin
+      return Expr.build [:number, evaluate]
+    rescue UndefinedVariableError
+      if Expr.build(operand1).simplify == Expr.build([:number, 0])
+        return Expr.build(operand2).simplify
+      elsif Expr.build(operand2).simplify == Expr.build([:number, 0])
+        return Expr.build(operand1).simplify
+      else
+        return Expr.build(operand1).simplify + Expr.build(operand2).simplify
+      end
+    end
+  end
+
+  def derive(variable)
+    Expr.build(operand1).derive(variable) + Expr.build(operand2).derive(variable)
+  end
+end
+
+class Multiplication < Binary
+  def evaluate(environment = {})
+    Expr.build(operand1).evaluate(environment) * Expr.build(operand2).evaluate(environment)
+  end
+
+  def simplify
+    begin
+      return Expr.build [:number, evaluate]
+    rescue UndefinedVariableError
+      if Expr.build(operand1).simplify == Expr.build([:number, 1])
+        return Expr.build(operand2).simplify
+      elsif Expr.build(operand2).simplify == Expr.build([:number, 1])
+        return Expr.build(operand1).simplify
+      elsif Expr.build(operand1).simplify == Expr.build([:number, 0]) or
+            Expr.build(operand2).simplify == Expr.build([:number, 0])
+        return Expr.build [:number, 0]
+      else
+        return Expr.build(operand1).simplify * Expr.build(operand2).simplify
+      end
+    end
+  end
+
+  def derive(variable)
+    Expr.build(operand1).derive(variable) * Expr.build(operand2) +
+    Expr.build(operand1) * Expr.build(operand2).derive(variable)
+  end
+end
+
+class Negation < Unary
+  def evaluate(environment = {})
+    - Expr.build(operand).evaluate(environment)
+  end
+
+  def simplify
+    begin
+      return Expr.build [:number, evaluate]
+    rescue UndefinedVariableError
+      return - Expr.build(operand).simplify
+    end
+  end
+
+  def derive(variable)
+    - Expr.build(operand).derive(variable)
+  end
+end
+
+class Sine < Unary
+  def evaluate(environment = {})
+    Math.sin Expr.build(operand).evaluate(environment)
+  end
+
+  def simplify
+    begin
+      return Expr.build [:number, evaluate]
+    rescue UndefinedVariableError
+      return Expr.build [:sin, Expr.build(operand).simplify.s_expr]
+    end
+  end
+
+  def derive(variable)
+    Expr.build(operand).derive(variable) * Cosine.build(operand)
+  end
+end
+
+class Cosine < Unary
+  def evaluate(environment = {})
+    Math.cos Expr.build(operand).evaluate(environment)
+  end
+
+  def simplify
+    begin
+      return Expr.build [:number, evaluate]
+    rescue UndefinedVariableError
+      return Expr.build [:cos, Expr.build(operand).simplify.s_expr]
+    end
+  end
+
+  def derive(variable)
+    Expr.build(operand).derive(variable) * (- Sine.build(operand))
+  end
+end
