# frozen_string_literal: true module RubyAlgebra class Expression def to_s raise NotImplementedError end def type raise NotImplementedError end def op_priority raise NotImplementedError end def op_assoc_type raise NotImplementedError end def diff(v) raise NotImplementedError, "#{self.class}#diff not implemented" end def evaluate raise NotImplementedError, "#{self.class}#evaluate not implemented" end def constant? raise NotImplementedError, "#{self.class}#constant? not implemented" end end class Addition < Expression attr_reader :lhs, :rhs def initialize(lhs, rhs) raise ArgumentError unless lhs.is_a?(Expression) && rhs.is_a?(Expression) @lhs = lhs @rhs = rhs end def to_s need_parentheses_left = lhs.op_priority < op_priority || (lhs.op_priority == op_priority && op_assoc_type == :right) need_parentheses_right = rhs.op_priority < op_priority || (rhs.op_priority == op_priority && op_assoc_type == :left) result = need_parentheses_left ? "(#{lhs}) + " : "#{lhs} + " result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s) end def type :add end def op_priority 10 end def op_assoc_type :left end def ==(other) return false if other.type != type lhs == other.lhs && rhs == other.rhs end def diff(v) Addition.new(@lhs.diff(v), @rhs.diff(v)) end def evaluate lhs_val = @lhs.evaluate rhs_val = @rhs.evaluate return nil if lhs_val.nil? || rhs_val.nil? lhs_val + rhs_val end def constant? @lhs.constant? && @rhs.constant? end end class Subtraction < Addition attr_reader :lhs, :rhs def to_s need_parentheses_left = lhs.op_priority < op_priority || (lhs.op_priority == op_priority && op_assoc_type == :right) need_parentheses_right = rhs.op_priority < op_priority || (rhs.op_priority == op_priority && op_assoc_type == :left) result = need_parentheses_left ? "(#{lhs}) - " : "#{lhs} - " result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s) end def type :sub end def diff(v) Subtraction.new(@lhs.diff(v), @rhs.diff(v)) end def evaluate lhs_val = @lhs.evaluate rhs_val = @rhs.evaluate return nil if lhs_val.nil? || rhs_val.nil? lhs_val - rhs_val end def constant? @lhs.constant? && @rhs.constant? end end class Multiplication < Expression attr_reader :lhs, :rhs def initialize(lhs, rhs) raise ArgumentError unless lhs.is_a?(Expression) && rhs.is_a?(Expression) @lhs = lhs @rhs = rhs end def to_s need_parentheses_left = lhs.op_priority < op_priority || (lhs.op_priority == op_priority && op_assoc_type == :right) need_parentheses_right = rhs.op_priority < op_priority || (rhs.op_priority == op_priority && op_assoc_type == :left) if lhs.type == :constant && lhs.value == 1 need_parentheses_right ? "(#{rhs})" : rhs.to_s elsif lhs.type == :constant && lhs.value == -1 need_parentheses_right ? "-(#{rhs})" : "-#{rhs.to_s}" elsif (need_parentheses_right || rhs.type == :variable) && (need_parentheses_left || lhs.type == :mult || lhs.type == :constant) result = need_parentheses_left ? "(#{lhs})" : lhs.to_s if lhs.type == :variable && rhs.type == :variable && lhs.single_letter? && rhs.single_letter? result += " " end result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s) else result = need_parentheses_left ? "(#{lhs}) * " : "#{lhs} * " result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s) end end def type :mult end def op_priority 20 end def op_assoc_type :left end def ==(other) return false if other.type != type lhs == other.lhs && rhs == other.rhs end def diff(v) u_prime = @lhs.diff(v) v_prime = @rhs.diff(v) term1 = Multiplication.new(u_prime, @rhs) term2 = Multiplication.new(@lhs, v_prime) Addition.new(term1, term2) end def evaluate lhs_val = @lhs.evaluate rhs_val = @rhs.evaluate return nil if lhs_val.nil? || rhs_val.nil? lhs_val * rhs_val end def constant? @lhs.constant? && @rhs.constant? end end class Division < Multiplication def to_s need_parentheses_left = lhs.op_priority < op_priority || (lhs.op_priority == op_priority && op_assoc_type == :right) need_parentheses_right = rhs.op_priority < op_priority || (rhs.op_priority == op_priority && op_assoc_type == :left) result = need_parentheses_left ? "(#{lhs}) / " : "#{lhs} / " result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s) end def type :div end def diff(v) u_prime = @lhs.diff(v) v_prime = @rhs.diff(v) numerator = Subtraction.new( Multiplication.new(u_prime, @rhs), Multiplication.new(@lhs, v_prime) ) denominator = Power.new(@rhs, Constant.new(2)) Division.new(numerator, denominator) end def evaluate lhs_val = @lhs.evaluate rhs_val = @rhs.evaluate return nil if lhs_val.nil? || rhs_val.nil? return nil if rhs_val == 0 lhs_val / rhs_val end def constant? @lhs.constant? && @rhs.constant? end end class Power < Expression attr_reader :base, :exponent def initialize(base, exponent) raise ArgumentError unless base.is_a?(Expression) && exponent.is_a?(Expression) @base = base @exponent = exponent end def to_s need_parentheses_left = base.op_priority < op_priority || (base.op_priority == op_priority && op_assoc_type == :right) need_parentheses_right = exponent.op_priority < op_priority || (exponent.op_priority == op_priority && op_assoc_type == :left) result = need_parentheses_left ? "(#{base}) ^ " : "#{base} ^ " result + (need_parentheses_right ? "(#{exponent})" : exponent.to_s) end def type :pow end def op_priority 30 end def op_assoc_type :right end def ==(other) return false if other.type != type base == other.base && exponent == other.exponent end def diff(v) unless @exponent.is_a?(Constant) raise NotImplementedError, "Дифференцирование степени с неконстантным показателем не реализовано" end n = @exponent.value # (u^n)' = n * u^(n-1) * u' base_diff = @base.diff(v) new_power = Power.new(@base, Constant.new(n - 1)) coeff = Constant.new(n) Multiplication.new(coeff, Multiplication.new(new_power, base_diff)) end def evaluate base_val = @base.evaluate exp_val = @exponent.evaluate return nil if base_val.nil? || exp_val.nil? base_val ** exp_val end def constant? @base.constant? && @exponent.constant? end end class Constant < Expression attr_reader :value def initialize(value) @value = value end def to_s @value.to_s end def type :constant end def op_priority 1000 end def op_assoc_type nil end def ==(other) return false if other.type != type value == other.value end def diff(v) Constant.new(0) end def evaluate @value end def constant? true end end class Variable < Expression attr_reader :symbol def initialize(symbol) @symbol = symbol @is_single_letter = symbol.length == 1 end def to_s @symbol end def type :variable end def op_priority 1000 end def op_assoc_type nil end def single_letter? @is_single_letter end def ==(other) return false if other.type != type symbol == other.symbol end def diff(v) @symbol == v ? Constant.new(1) : Constant.new(0) end def evaluate nil end def constant? false end end end