Files
RubyAlgebra/lib/ruby_algebra/expression.rb

252 lines
6.0 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
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 diff(v)
Addition.new(@lhs.diff(v), @rhs.diff(v))
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
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 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
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
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 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
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 diff(v)
Constant.new(0)
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 diff(v)
@symbol == v ? Constant.new(1) : Constant.new(0)
end
end
end