283 lines
6.6 KiB
Ruby
283 lines
6.6 KiB
Ruby
# 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 ==(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
|
||
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 ==(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
|
||
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 ==(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
|
||
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
|
||
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
|
||
end
|
||
end
|