Files
RubyAlgebra/lib/ruby_algebra/expression.rb
2026-04-06 10:53:01 +03:00

376 lines
8.1 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
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}"
elsif (need_parentheses_right || rhs.type == :variable) && (need_parentheses_left || lhs.type == :mult || lhs.type == :constant)
result = need_parentheses_left ? "(#{lhs})" : lhs.to_s
result += ' ' if lhs.type == :variable && rhs.type == :variable && lhs.single_letter? && rhs.single_letter?
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.to_f / 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