implement interpreter and demo + bug fixes
- implement interpreter and demo - replace StandardError with appropriate subclasses - implement division - rewrite Polynomial.simplify()
This commit is contained in:
34
bin/demo
34
bin/demo
@@ -4,21 +4,27 @@
|
|||||||
require 'bundler/setup'
|
require 'bundler/setup'
|
||||||
require 'ruby_algebra'
|
require 'ruby_algebra'
|
||||||
|
|
||||||
puts 'RubyMaple © RubyMaple Creators, 2026. No rights reserved.'
|
print "RubyMaple © RubyMaple Creators, 2026. No rights reserved.\nPress Ctrl+D to quit.\n"
|
||||||
|
|
||||||
|
interp = RubyAlgebra::Interpreter.new
|
||||||
loop do
|
loop do
|
||||||
puts 'Введите формулу (пустая строка для выхода): '
|
print '> '
|
||||||
inp = $stdin.gets.chomp
|
inp = $stdin.gets
|
||||||
exit if inp.empty?
|
break if inp.nil?
|
||||||
begin
|
|
||||||
formula = RubyAlgebra::Parser.parse(inp)
|
|
||||||
rescue StandardError
|
|
||||||
puts 'Ошибка синтаксиса'
|
|
||||||
end
|
|
||||||
puts "Формула: #{formula}"
|
|
||||||
puts "Производная по x: #{formula.diff('x')}"
|
|
||||||
puts "Производная по y: #{formula.diff('y')}"
|
|
||||||
|
|
||||||
value = formula.evaluate
|
inp = inp.chomp
|
||||||
puts "Числовое значение: #{value}" unless value.nil?
|
next if inp.empty?
|
||||||
|
|
||||||
|
begin
|
||||||
|
command = RubyAlgebra::Parser.parse_command(inp)
|
||||||
|
rescue StandardError => e
|
||||||
|
puts "Syntax error: #{e.message}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
puts "< #{interp.execute(command)}"
|
||||||
|
rescue StandardError => e
|
||||||
|
puts "Calculation error: #{e.message}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
puts
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ require_relative 'ruby_algebra/version'
|
|||||||
require_relative 'ruby_algebra/command'
|
require_relative 'ruby_algebra/command'
|
||||||
require_relative 'ruby_algebra/polynomial'
|
require_relative 'ruby_algebra/polynomial'
|
||||||
require_relative 'ruby_algebra/parser'
|
require_relative 'ruby_algebra/parser'
|
||||||
|
require_relative 'ruby_algebra/interpreter'
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
module RubyAlgebra
|
module RubyAlgebra
|
||||||
# Команда интерпретатора
|
# Команда интерпретатора
|
||||||
class Command
|
class Command
|
||||||
|
attr_reader :type
|
||||||
|
|
||||||
|
def type
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Команда присвоения
|
# Команда присвоения
|
||||||
@@ -39,20 +44,29 @@ module RubyAlgebra
|
|||||||
end
|
end
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
:assignment
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Команда вывода на экран
|
# Команда вывода на экран
|
||||||
class DisplayCommand < Command
|
class DisplayCommand < Command
|
||||||
attr_reader :polynomial
|
attr_reader :item
|
||||||
|
|
||||||
def initialize(polynomial)
|
def initialize(item)
|
||||||
@polynomial = polynomial
|
@type = :display
|
||||||
|
@item = item
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
result = "Display command: "
|
result = "Display command: "
|
||||||
result += @polynomial.to_s
|
result += @item.to_s
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
:display
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
70
lib/ruby_algebra/interpreter.rb
Normal file
70
lib/ruby_algebra/interpreter.rb
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
module RubyAlgebra
|
||||||
|
class Interpreter
|
||||||
|
def initialize
|
||||||
|
@locals = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute(command)
|
||||||
|
case command.type
|
||||||
|
when :assignment
|
||||||
|
unless command.operand1.is_a?(Polynomial)
|
||||||
|
operand1 = @locals[command.operand1]
|
||||||
|
return "Undeclared polynomial: #{command.operand1}" if operand1.nil?
|
||||||
|
else
|
||||||
|
operand1 = command.operand1
|
||||||
|
end
|
||||||
|
|
||||||
|
unless [:assign, :scale, :diff, :subs].include?(command.operation)
|
||||||
|
operand2 = @locals[command.operand2]
|
||||||
|
return "Undeclared polynomial: #{command.operand2}" if operand2.nil?
|
||||||
|
else
|
||||||
|
operand2 = command.operand2
|
||||||
|
end
|
||||||
|
|
||||||
|
case command.operation
|
||||||
|
when :assign
|
||||||
|
@locals[command.lhs] = operand1
|
||||||
|
return "#{command.lhs} = #{operand1}"
|
||||||
|
when :add
|
||||||
|
result = operand1 + operand2
|
||||||
|
@locals[command.lhs] = result
|
||||||
|
return "#{command.lhs} = #{result}"
|
||||||
|
when :sub
|
||||||
|
result = operand1 - operand2
|
||||||
|
@locals[command.lhs] = result
|
||||||
|
return "#{command.lhs} = #{result}"
|
||||||
|
when :mult, :scale
|
||||||
|
result = operand1 * operand2
|
||||||
|
@locals[command.lhs] = result
|
||||||
|
return "#{command.lhs} = #{result}"
|
||||||
|
when :div
|
||||||
|
result = operand1 / operand2
|
||||||
|
@locals[command.lhs[0]] = result[0]
|
||||||
|
@locals[command.lhs[1]] = result[1]
|
||||||
|
return "Quotient: #{command.lhs[0]} = #{result[0]}, remainder: #{command.lhs[1]} = #{result[1]}"
|
||||||
|
when :diff
|
||||||
|
result = operand1
|
||||||
|
operand2.each do |variable|
|
||||||
|
result = result.diff(variable)
|
||||||
|
end
|
||||||
|
@locals[command.lhs] = result
|
||||||
|
return "#{command.lhs} = #{result}"
|
||||||
|
when :subs
|
||||||
|
result = operand1.evaluate(operand2)
|
||||||
|
@locals[command.lhs] = result
|
||||||
|
return "#{command.lhs} = #{result}"
|
||||||
|
end
|
||||||
|
when :display
|
||||||
|
unless command.item.is_a? Polynomial
|
||||||
|
unless @locals[command.item].nil?
|
||||||
|
return @locals[command.item].to_s
|
||||||
|
else
|
||||||
|
return "Undeclared polynomial: #{command.item}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return command.item.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -118,6 +118,9 @@ module RubyAlgebra
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class TokenizerError < StandardError; end
|
||||||
|
class ParserError < StandardError; end
|
||||||
|
|
||||||
# Класс для разбора выражений на токены
|
# Класс для разбора выражений на токены
|
||||||
class Tokenizer
|
class Tokenizer
|
||||||
attr_reader :lookahead
|
attr_reader :lookahead
|
||||||
@@ -135,22 +138,16 @@ module RubyAlgebra
|
|||||||
end
|
end
|
||||||
|
|
||||||
def _next_token
|
def _next_token
|
||||||
puts '_next_token'
|
|
||||||
|
|
||||||
loop do
|
loop do
|
||||||
return EndToken.new unless @i < @expr.length
|
return EndToken.new unless @i < @expr.length
|
||||||
puts "i = #{@i}"
|
|
||||||
c = @expr[@i]
|
c = @expr[@i]
|
||||||
puts "c = #{c}"
|
|
||||||
if c.match?(/\p{Alpha}/)
|
if c.match?(/\p{Alpha}/)
|
||||||
puts 'letter'
|
|
||||||
j = @i + 1
|
j = @i + 1
|
||||||
j += 1 while j < @expr.length && @expr[j].match?(/\p{Alnum}/)
|
j += 1 while j < @expr.length && @expr[j].match?(/\p{Alnum}/)
|
||||||
token = IdentifierToken.new(@expr[@i...j])
|
token = IdentifierToken.new(@expr[@i...j])
|
||||||
@i = j
|
@i = j
|
||||||
return token
|
return token
|
||||||
elsif c.match(%r{[+\-*/^():,=]})
|
elsif c.match(%r{[+\-*/^():,=]})
|
||||||
puts 'operator'
|
|
||||||
token = nil
|
token = nil
|
||||||
case c
|
case c
|
||||||
when '+'
|
when '+'
|
||||||
@@ -179,21 +176,17 @@ module RubyAlgebra
|
|||||||
end
|
end
|
||||||
return token
|
return token
|
||||||
elsif c.match?(/\p{Digit}|\./)
|
elsif c.match?(/\p{Digit}|\./)
|
||||||
puts 'digit'
|
|
||||||
j = @i + 1
|
j = @i + 1
|
||||||
j += 1 while j < @expr.length && @expr[j].match?(/\p{Digit}|\./)
|
j += 1 while j < @expr.length && @expr[j].match?(/\p{Digit}|\./)
|
||||||
s = @expr[@i...j]
|
s = @expr[@i...j]
|
||||||
@i = j
|
@i = j
|
||||||
puts "ssss: #{s}"
|
|
||||||
fl = s.count '.'
|
fl = s.count '.'
|
||||||
raise StandardError, 'malformed number' if fl > 1
|
raise TokenizerError, 'malformed number' if fl > 1
|
||||||
return fl.zero? ? NumberToken.new(s.to_i) : NumberToken.new(s.to_f)
|
return fl.zero? ? NumberToken.new(s.to_i) : NumberToken.new(s.to_f)
|
||||||
elsif c == ' '
|
elsif c == ' '
|
||||||
puts 'whitespace'
|
|
||||||
@i += 1
|
@i += 1
|
||||||
else
|
else
|
||||||
puts 'unknown'
|
raise TokenizerError, "unrecognized character '#{c}'"
|
||||||
@i += 1
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -217,7 +210,7 @@ module RubyAlgebra
|
|||||||
tokenizer.next_token
|
tokenizer.next_token
|
||||||
n = tokenizer.next_token
|
n = tokenizer.next_token
|
||||||
unless n.type == :paren && n.closing == false
|
unless n.type == :paren && n.closing == false
|
||||||
raise StandardError, "unexpected token #{n.type}, expected ("
|
raise ParserError, "unexpected token #{n.type}, expected ("
|
||||||
end
|
end
|
||||||
n = tokenizer.lookahead
|
n = tokenizer.lookahead
|
||||||
if n.type == :id && !n.variable?
|
if n.type == :id && !n.variable?
|
||||||
@@ -231,23 +224,23 @@ module RubyAlgebra
|
|||||||
tokenizer.next_token
|
tokenizer.next_token
|
||||||
n = tokenizer.next_token
|
n = tokenizer.next_token
|
||||||
unless n.type == :id && n.variable?
|
unless n.type == :id && n.variable?
|
||||||
raise StandardError, "unexpected token #{n.type}, expected variable"
|
raise ParserError, "unexpected token #{n.type}, expected variable"
|
||||||
end
|
end
|
||||||
diff_variables << n.symbol
|
diff_variables << n.symbol
|
||||||
end
|
end
|
||||||
n = tokenizer.next_token
|
n = tokenizer.next_token
|
||||||
unless n.type == :paren && n.closing == true
|
unless n.type == :paren && n.closing == true
|
||||||
raise StandardError, "unexpected token #{n.type}, expected )"
|
raise ParserError, "unexpected token #{n.type}, expected )"
|
||||||
end
|
end
|
||||||
unless tokenizer.next_token.type == :end
|
unless tokenizer.next_token.type == :end
|
||||||
raise StandardError, "unexpected token at the end"
|
raise ParserError, "unexpected token at the end"
|
||||||
end
|
end
|
||||||
return AssignmentCommand.new(left_hand_side, :diff, target, diff_variables)
|
return AssignmentCommand.new(left_hand_side, :diff, target, diff_variables)
|
||||||
elsif operand1.symbol == 'Subs'
|
elsif operand1.symbol == 'Subs'
|
||||||
tokenizer.next_token
|
tokenizer.next_token
|
||||||
n = tokenizer.next_token
|
n = tokenizer.next_token
|
||||||
unless n.type == :paren && n.closing == false
|
unless n.type == :paren && n.closing == false
|
||||||
raise StandardError, "unexpected token #{n.type}, expected ("
|
raise ParserError, "unexpected token #{n.type}, expected ("
|
||||||
end
|
end
|
||||||
n = tokenizer.lookahead
|
n = tokenizer.lookahead
|
||||||
if n.type == :id && !n.variable?
|
if n.type == :id && !n.variable?
|
||||||
@@ -261,35 +254,35 @@ module RubyAlgebra
|
|||||||
tokenizer.next_token
|
tokenizer.next_token
|
||||||
n = tokenizer.next_token
|
n = tokenizer.next_token
|
||||||
unless n.type == :id && n.variable?
|
unless n.type == :id && n.variable?
|
||||||
raise StandardError, "unexpected token #{n.type}, expected variable"
|
raise ParserError, "unexpected token #{n.type}, expected variable"
|
||||||
end
|
end
|
||||||
variable = n.symbol
|
variable = n.symbol
|
||||||
unless tokenizer.next_token.type == :equals
|
unless tokenizer.next_token.type == :equals
|
||||||
raise StandardError, "unexpected token #{n.type}, expected ="
|
raise ParserError, "unexpected token #{n.type}, expected ="
|
||||||
end
|
end
|
||||||
n = tokenizer.next_token
|
n = tokenizer.next_token
|
||||||
unless n.type == :num
|
unless n.type == :num
|
||||||
raise StandardError, "unexpected token #{n.type}, expected number"
|
raise ParserError, "unexpected token #{n.type}, expected number"
|
||||||
end
|
end
|
||||||
substitutions[variable] = n.value
|
substitutions[variable] = n.value
|
||||||
end
|
end
|
||||||
n = tokenizer.next_token
|
n = tokenizer.next_token
|
||||||
unless n.type == :paren && n.closing == true
|
unless n.type == :paren && n.closing == true
|
||||||
raise StandardError, "unexpected token #{n.type}, expected )"
|
raise ParserError, "unexpected token #{n.type}, expected )"
|
||||||
end
|
end
|
||||||
unless tokenizer.next_token.type == :end
|
unless tokenizer.next_token.type == :end
|
||||||
raise StandardError, "unexpected token at the end"
|
raise ParserError, "unexpected token at the end"
|
||||||
end
|
end
|
||||||
return AssignmentCommand.new(left_hand_side, :subs, target, substitutions)
|
return AssignmentCommand.new(left_hand_side, :subs, target, substitutions)
|
||||||
else
|
else
|
||||||
tokenizer.next_token
|
tokenizer.next_token
|
||||||
operator = tokenizer.next_token
|
operator = tokenizer.next_token
|
||||||
raise StandardError, "unexpected token #{operator.type}, expected +, -, * or /" unless [:plus, :minus, :mult, :div].include?(operator.type)
|
raise ParserError, "unexpected token #{operator.type}, expected +, -, * or /" unless [:plus, :minus, :mult, :div].include?(operator.type)
|
||||||
|
|
||||||
operand2 = tokenizer.next_token
|
operand2 = tokenizer.next_token
|
||||||
unless ([:plus, :minus, :mult].include?(operator.type) && operand2.type == :id && !operand2.variable?) ||
|
unless ([:plus, :minus, :mult].include?(operator.type) && operand2.type == :id && !operand2.variable?) ||
|
||||||
([:mult, :div].include?(operator.type) && operand2.type == :num)
|
([:mult, :div].include?(operator.type) && operand2.type == :num)
|
||||||
raise StandardError, 'unsupported operation or invalid syntax'
|
raise ParserError, 'unsupported operation or invalid syntax'
|
||||||
end
|
end
|
||||||
if operand2.type == :num
|
if operand2.type == :num
|
||||||
if operator.type == :mult
|
if operator.type == :mult
|
||||||
@@ -315,49 +308,52 @@ module RubyAlgebra
|
|||||||
n = tokenizer.next_token
|
n = tokenizer.next_token
|
||||||
second_lhs = tokenizer.next_token
|
second_lhs = tokenizer.next_token
|
||||||
unless second_lhs.type == :id && !second_lhs.variable?
|
unless second_lhs.type == :id && !second_lhs.variable?
|
||||||
raise StandardError, "unexpected token #{second_lhs.type}, expected polynomial variable"
|
raise ParserError, "unexpected token #{second_lhs.type}, expected polynomial variable"
|
||||||
end
|
end
|
||||||
unless tokenizer.next_token.type == :assign
|
unless tokenizer.next_token.type == :assign
|
||||||
raise StandardError, "unexpected token, expected :="
|
raise ParserError, "unexpected token, expected :="
|
||||||
end
|
end
|
||||||
operand1 = tokenizer.next_token
|
operand1 = tokenizer.next_token
|
||||||
unless operand1.type == :id && !operand1.variable?
|
unless operand1.type == :id && !operand1.variable?
|
||||||
raise StandardError, "unexpected token #{operand1.type}, expected polynomial variable"
|
raise ParserError, "unexpected token #{operand1.type}, expected polynomial variable"
|
||||||
end
|
end
|
||||||
unless tokenizer.next_token.type == :div
|
unless tokenizer.next_token.type == :div
|
||||||
raise StandardError, "unexpected token, expected /"
|
raise ParserError, "unexpected token, expected /"
|
||||||
end
|
end
|
||||||
operand2 = tokenizer.next_token
|
operand2 = tokenizer.next_token
|
||||||
unless operand2.type == :id && !operand2.variable?
|
unless operand2.type == :id && !operand2.variable?
|
||||||
raise StandardError, "unexpected token #{operand2.type}, expected polynomial variable"
|
raise ParserError, "unexpected token #{operand2.type}, expected polynomial variable"
|
||||||
end
|
end
|
||||||
return AssignmentCommand.new([left_hand_side, second_lhs.symbol], :div, operand1.symbol)
|
return AssignmentCommand.new([left_hand_side, second_lhs.symbol], :div, operand1.symbol, operand2.symbol)
|
||||||
elsif tokenizer.lookahead.type == :end
|
elsif tokenizer.lookahead.type == :end
|
||||||
return DisplayCommand.new(left_hand_side)
|
return DisplayCommand.new(left_hand_side)
|
||||||
else
|
else
|
||||||
raise StandardError, "unexpected token #{n.type}, expected ':=' or ','"
|
raise ParserError, "unexpected token #{n.type}, expected ':=' or ','"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.parse_polynomial(tokenizer)
|
def self.parse_polynomial(tokenizer)
|
||||||
puts "parse_polynomial <lookahead #{tokenizer.lookahead.type}>"
|
first_term_negative = false
|
||||||
terms = [parse_term(tokenizer)]
|
if tokenizer.lookahead.type == :plus || tokenizer.lookahead.type == :minus
|
||||||
|
first_term_negative = tokenizer.lookahead.type == :minus
|
||||||
|
tokenizer.next_token
|
||||||
|
end
|
||||||
|
first_term = parse_term(tokenizer)
|
||||||
|
first_term *= -1 if first_term_negative
|
||||||
|
terms = [first_term]
|
||||||
while [:plus, :minus].include?(tokenizer.lookahead.type)
|
while [:plus, :minus].include?(tokenizer.lookahead.type)
|
||||||
sign = tokenizer.next_token.type == :plus ? +1 : -1
|
sign = tokenizer.next_token.type == :plus ? +1 : -1
|
||||||
terms << parse_term(tokenizer) * sign
|
terms << parse_term(tokenizer) * sign
|
||||||
end
|
end
|
||||||
puts 'parse_polynomial end'
|
|
||||||
Polynomial.new(terms)
|
Polynomial.new(terms)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.parse_term(tokenizer)
|
def self.parse_term(tokenizer)
|
||||||
puts "parse_term <lookahead #{tokenizer.lookahead.type}>"
|
|
||||||
n = tokenizer.lookahead
|
n = tokenizer.lookahead
|
||||||
unless n.type == :id && n.variable? || n.type == :num
|
unless n.type == :id && n.variable? || n.type == :num
|
||||||
raise StandardError, "unexpected token #{n.type}, expected number or variable"
|
raise ParserError, "unexpected token #{n.type}, expected number or variable"
|
||||||
end
|
end
|
||||||
if n.type == :num
|
if n.type == :num
|
||||||
puts "eat coeff"
|
|
||||||
tokenizer.next_token
|
tokenizer.next_token
|
||||||
coeff = n.value
|
coeff = n.value
|
||||||
variables = {}
|
variables = {}
|
||||||
@@ -368,44 +364,34 @@ module RubyAlgebra
|
|||||||
end
|
end
|
||||||
loop do
|
loop do
|
||||||
n = tokenizer.lookahead
|
n = tokenizer.lookahead
|
||||||
puts "<lookahead #{n.type}>"
|
|
||||||
unless n.type == :mult || n.type == :id && n.variable?
|
unless n.type == :mult || n.type == :id && n.variable?
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
v = parse_variable_factor(tokenizer)
|
v = parse_variable_factor(tokenizer)
|
||||||
variables[v[0]] = v[1]
|
variables[v[0]] = v[1]
|
||||||
end
|
end
|
||||||
puts 'parse_term end'
|
|
||||||
pp [coeff, variables]
|
|
||||||
Term.new(coeff, variables)
|
Term.new(coeff, variables)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.parse_variable_factor(tokenizer)
|
def self.parse_variable_factor(tokenizer)
|
||||||
puts "parse_term <lookahead #{tokenizer.lookahead.type}>"
|
|
||||||
n = tokenizer.lookahead
|
n = tokenizer.lookahead
|
||||||
tokenizer.next_token
|
tokenizer.next_token
|
||||||
puts 'eat variable'
|
|
||||||
if n.type == :mult
|
if n.type == :mult
|
||||||
n = tokenizer.next_token
|
n = tokenizer.next_token
|
||||||
puts 'eat *'
|
|
||||||
end
|
end
|
||||||
unless n.type == :id && n.variable?
|
unless n.type == :id && n.variable?
|
||||||
raise StandardError, "unexpected token #{n.type}, expected variable"
|
raise ParserError, "unexpected token #{n.type}, expected variable"
|
||||||
end
|
end
|
||||||
variable = n.symbol
|
variable = n.symbol
|
||||||
if tokenizer.lookahead.type == :pow
|
if tokenizer.lookahead.type == :pow
|
||||||
tokenizer.next_token
|
tokenizer.next_token
|
||||||
puts 'eat ^'
|
|
||||||
unless tokenizer.lookahead.type == :num
|
unless tokenizer.lookahead.type == :num
|
||||||
raise StandardError, "unexpected token #{n.type}, expected number"
|
raise ParserError, "unexpected token #{n.type}, expected number"
|
||||||
end
|
end
|
||||||
power = tokenizer.next_token.value
|
power = tokenizer.next_token.value
|
||||||
puts 'eat exponent'
|
|
||||||
else
|
else
|
||||||
power = 1
|
power = 1
|
||||||
end
|
end
|
||||||
puts 'parse_variable_factor end'
|
|
||||||
pp [variable, power]
|
|
||||||
[variable, power]
|
[variable, power]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ module RubyAlgebra
|
|||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
if @terms.empty?
|
if @terms.empty?
|
||||||
result = ''
|
result = '0'
|
||||||
else
|
else
|
||||||
result = @terms[-1].to_s
|
result = @terms[-1].to_s
|
||||||
i = @terms.length - 2
|
i = @terms.length - 2
|
||||||
@@ -37,7 +37,7 @@ module RubyAlgebra
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
def evaluate(values)
|
def substitute(values)
|
||||||
result = 0
|
result = 0
|
||||||
@terms.each do |term|
|
@terms.each do |term|
|
||||||
# ...
|
# ...
|
||||||
@@ -67,6 +67,50 @@ module RubyAlgebra
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def /(other)
|
||||||
|
raise StandardError, "cannot divide by zero" if other.zero?
|
||||||
|
return [Polynomial.new, Polynomial.new] if self.zero?
|
||||||
|
|
||||||
|
# Сначала проверить, что оба многочлена от одной и той же переменной
|
||||||
|
t = if @terms.empty? || @terms[-1].variables.empty? then other.terms[-1] else @terms[-1] end
|
||||||
|
unless t.variables.empty?
|
||||||
|
only_variable = t.variables.keys[0]
|
||||||
|
@terms.each do |term|
|
||||||
|
next if term.variables.empty?
|
||||||
|
unless term.variables.length == 1 && term.variables.keys[0] == only_variable
|
||||||
|
raise StandardError, "cannot divide polynomials with different variables"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
other.terms.each do |term|
|
||||||
|
next if term.variables.empty?
|
||||||
|
unless term.variables.length == 1 && term.variables.keys[0] == only_variable
|
||||||
|
raise StandardError, "cannot divide polynomials with different variables"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
quotient = Polynomial.new
|
||||||
|
remainder = self
|
||||||
|
divisor_power = unless other.terms.last.variables.empty? then other.terms.last.variables.first[1] else 0 end
|
||||||
|
loop do
|
||||||
|
break if remainder.zero?
|
||||||
|
|
||||||
|
dividend_power = unless remainder.terms.last.variables.empty? then remainder.terms.last.variables.first[1] else 0 end
|
||||||
|
break if dividend_power < divisor_power
|
||||||
|
|
||||||
|
result_power = dividend_power - divisor_power
|
||||||
|
result_coeff = remainder.terms.last.coeff.to_f / other.terms.last.coeff
|
||||||
|
if result_power > 0
|
||||||
|
q = Term.new(result_coeff, {only_variable => result_power})
|
||||||
|
else
|
||||||
|
q = Term.new(result_coeff, {})
|
||||||
|
end
|
||||||
|
quotient += q
|
||||||
|
remainder -= other * q
|
||||||
|
end
|
||||||
|
[quotient, remainder]
|
||||||
|
end
|
||||||
|
|
||||||
def **(other)
|
def **(other)
|
||||||
result = Polynomial.new([Term.new(1)])
|
result = Polynomial.new([Term.new(1)])
|
||||||
other.times do
|
other.times do
|
||||||
@@ -91,50 +135,40 @@ module RubyAlgebra
|
|||||||
@terms == other.terms
|
@terms == other.terms
|
||||||
end
|
end
|
||||||
|
|
||||||
def simplify
|
def _simplify
|
||||||
i = 0
|
i = 0
|
||||||
new_terms = @terms.filter { |t| !t.zero? }
|
new_terms = @terms.filter { |t| !t.zero? }
|
||||||
while i < new_terms.length
|
while i < new_terms.length
|
||||||
j = i + 1
|
j = i + 1
|
||||||
|
annihilated = false
|
||||||
while j < new_terms.length
|
while j < new_terms.length
|
||||||
if new_terms[i].similar_to?(new_terms[j])
|
if new_terms[i].similar_to?(new_terms[j])
|
||||||
new_terms[i] += new_terms[j]
|
new_terms[i] += new_terms[j]
|
||||||
|
new_terms.delete_at(j)
|
||||||
if new_terms[i].zero?
|
if new_terms[i].zero?
|
||||||
new_terms.delete_at(i)
|
new_terms.delete_at(i)
|
||||||
i -= 1
|
annihilated = true
|
||||||
j -= 1
|
break
|
||||||
end
|
end
|
||||||
new_terms.delete_at(j)
|
else
|
||||||
j -= 1
|
j += 1
|
||||||
end
|
end
|
||||||
j += 1
|
|
||||||
end
|
end
|
||||||
i += 1
|
i += 1 unless annihilated
|
||||||
end
|
end
|
||||||
Polynomial.new(new_terms.sort_by { |t| t.total_power })
|
new_terms.sort_by { |t| t.total_power }
|
||||||
|
end
|
||||||
|
|
||||||
|
def simplify
|
||||||
|
Polynomial.new(_simplify)
|
||||||
end
|
end
|
||||||
|
|
||||||
def simplify!
|
def simplify!
|
||||||
i = 0
|
@terms = _simplify
|
||||||
new_terms = @terms.filter { |t| !t.zero? }
|
end
|
||||||
while i < new_terms.length
|
|
||||||
j = i + 1
|
def zero?
|
||||||
while j < new_terms.length
|
@terms.all? { |t| t.zero? }
|
||||||
if new_terms[i].similar_to?(new_terms[j])
|
|
||||||
new_terms[i] += new_terms[j]
|
|
||||||
if new_terms[i].zero?
|
|
||||||
new_terms.delete_at(i)
|
|
||||||
i -= 1
|
|
||||||
j -= 1
|
|
||||||
end
|
|
||||||
new_terms.delete_at(j)
|
|
||||||
j -= 1
|
|
||||||
end
|
|
||||||
j += 1
|
|
||||||
end
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
@terms = new_terms.sort_by { |t| t.total_power }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -160,7 +194,7 @@ module RubyAlgebra
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
def evaluate(values)
|
def substitute(values)
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -186,6 +220,11 @@ module RubyAlgebra
|
|||||||
Term.new(@coeff * other.coeff, new_variables)
|
Term.new(@coeff * other.coeff, new_variables)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def /(other)
|
||||||
|
raise StandardError, "cannot divide by zero" if other.zero?
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
def +@
|
def +@
|
||||||
Term.new(@coeff, @variables)
|
Term.new(@coeff, @variables)
|
||||||
end
|
end
|
||||||
@@ -203,7 +242,13 @@ module RubyAlgebra
|
|||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
result = @coeff != 1 ? @coeff.to_s : ''
|
if @coeff == 1 && !@variables.empty?
|
||||||
|
result = ''
|
||||||
|
elsif @coeff == -1 && !@variables.empty?
|
||||||
|
result = '-'
|
||||||
|
else
|
||||||
|
result = @coeff.to_s
|
||||||
|
end
|
||||||
unless @variables.empty?
|
unless @variables.empty?
|
||||||
first = true
|
first = true
|
||||||
@variables.each do |var, power|
|
@variables.each do |var, power|
|
||||||
|
|||||||
Reference in New Issue
Block a user