Илия обнови решението на 14.11.2012 16:57 (преди почти 13 години)
+
+class Expr
+  def self.build(array)
+    type = array[0]
+    return Negation.new(array)       if type == :-
+    return Addition.new(array)       if type == :+
+    return Multiplication.new(array) if type == :*
+    class_of_type = {number: Number, variable: Variable, sin: Sine, cos: Cosine}
+    class_of_type[type].new(array)
+  end
+
+  def +(other)
+    Addition.new([:+,self,other])
+  end
+
+  def *(other)
+    Multiplication.new([:*,self,other])
+  end
+
+  def -
+    Negation.new([:-,self])
+  end
+end
+
+class Unary < Expr
+  def initialize(array)
+    @type = array.shift
+    @a = Expr.build array.shift
+  end
+
+  def exact?
+    @a.exact?
+  end
+
+  def simplify
+    return [:number, evaluate] if exact?
+    Expr.build [@type, @a.simplify]
+  end
+end
+
+class Binary < Expr
+  def initialize(array)
+    @type = array.shift
+    @a = Expr.build array.shift
+    @b = Expr.build array.shift
+  end
+
+  def exact?
+    @a.exact? and @b.exact?
+  end
+
+  def simplify
+    return [:number, evaluate] if exact?
+    Expr.build [@type, @a.simplify, @b.simplify]
+  end
+end
+
+class Number < Unary
+  def initialize(array)
+    @type, @a = array
+  end
+
+  def exact?
+    true
+  end
+
+  def evaluate(environment = {})
+    @a
+  end
+
+  def simplify
+    self
+  end
+
+  def derive(variable)
+    Expr.build([:number,0])
+  end
+end
+
+class Variable < Unary
+  def initialize(array)
+    @type, @a = array
+  end
+
+  def exact?
+    false
+  end
+
+  def evaluate(environment = {})
+    return environment[@a] if environment[@a]
+    raise 'There is an uninitialized variable'
+  end
+
+  def simplify
+    self
+  end
+
+  def derive(variable)
+    return Expr.build([:number,1]) if @a == variable
+    Expr.build([:number,0])
+  end
+end
+
+class Negation < Unary
+  def evaluate(environment = {})
+    -@a.evaluate(environment)
+  end
+
+  def derive(variable)
+    -@a.derive(variable)
+  end
+end
+
+class Addition < Binary
+  def evaluate(environment = {})
+    @a.evaluate(environment) + @b.evaluate(environment)
+  end
+
+  def simplify
+    return [:number, evaluate] if exact?
+    return @b.simplify if @a.simplify.zero?
+    return @a.simplify if @b.simplify.zero?
+    Expr.build [@type, @a.simplify, @b.simplify]
+  end
+
+  def derive(variable)
+    @a.derive(variable) + @b.derive(variable)
+  end
+end
+
+class Multiplication < Binary
+  def evaluate(environment = {})
+    @a.evaluate(environment) * @b.evaluate(environment)
+  end
+
+  def simplify
+    return [:number, evaluate] if exact?
+    return @b.simplify if @a.simplify == 1
+    return @a.simplify if @b.simplify == 1
+    return 0 if @b.simplify.zero? or @a.simplify.zero?
+    Expr.build [@type, @a.simplify, @b.simplify]
+  end
+
+  def derive(variable)
+    @a.derive(variable) * @b + @a * @b.derive(variable)
+  end
+end
+
+class Sine < Unary
+  def evaluate(environment = {})
+    Math.sin @a.evaluate(environment)
+  end
+
+  def derive(variable)
+    @a.derive(variable) * Cosine.new(@a)
+  end
+end
+
+class Cosine < Unary
+  def evaluate(environment = {})
+    Math.cos @a.evaluate(environment)
+  end
+
+  def derive(variable)
+    @a.derive(variable) * (-Sine.new(@a))
+  end
+end
