#ALL_LEXEMS = [:open, :close, :operator, :number, :function, :var]
IDENTIFIER = /[a-zA-Z]\w*/
$registered_functions = {sin: 1, cos: 1, log: 1, exp: 1}
$registered_operators = {:+ => 1, :- => 1, :* => 2, :/ => 2, :^ => 3}
$unary = {:- => true, :+ => true}
$commutative = {:+ => true, :* => true}
def numeric?(object)
true if Float(object) rescue false
end
class Token
attr_accessor :typ
attr_accessor :data
def detect_type s
@data = s
return :open if s=="("
return :close if s==")"
return :number if numeric?(s)
return :function if $registered_functions[s.to_sym]
return :operator if $registered_operators[s.to_sym]
return :var if IDENTIFIER =~ s
throw "unrecognised token: #{s}"
end
def initialize s
@typ = detect_type(s)
end
def to_s
"[#{@typ}: #{@data}]"
end
end
def lexer string
string.scan(/(?:[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|[a-zA-Z]\w*|\S/).map{|x| Token.new(x)}
end
class Operation
attr_accessor :args, :parent, :data
def evaluate vars={}
throw :abstract
end
def simplify
# p "simplify: #{self}"
@args.map!(&:simplify)
self
end
def initialize parent,data
@data = data
@args = []
move_to(parent)
end
def move_to parent
@parent.args.delete(self) if @parent
@parent = parent
@parent.args << self if @parent
end
def to_s
@data.to_s
end
def priority
10000
end
def differentiate(var)
throw :abstract
end
def depends(var)
@args.any?{|a| a.depends(var)}
end
end
#syntax sugar
def make_op(*pseudo)
# p pseudo
case pseudo.length
when 1
if pseudo.first.is_a?(Symbol)
result = OpVariable.new(nil, pseudo.first)
elsif pseudo.first.is_a?(Operation)
result = pseudo.first
else
result = OpConstant.new(nil, pseudo.first)
end
when 2..3
if $registered_functions[pseudo[0]]
result = OpFunction.new(nil, pseudo[0])
result.args = [make_op(pseudo[1])]
result
else
result = OpBinary.new(nil, pseudo[1])
nums = pseudo.length > 2 ? [0,2] : [0]
result.args = nums.map{|x| make_op(pseudo[x])}
result
end
end
end
class OpConstant < Operation
def evaluate vars={}
@data
end
def differentiate(var)
make_op(0)
end
def to_s
@data.to_f.to_s
end
end
class OpDummy < Operation
def evaluate vars={}
@args[0].evaluate(vars)
end
def differentiate(var)
@args[0].differentiate(var)
end
def simplify
super
@args[0]
end
def to_s
"(#{@args[0]})"
end
def priority
0
end
end
class OpVariable < Operation
def evaluate vars={}
vars[@data]
end
def differentiate(var)
make_op(@data == var ? 1 : 0)
end
def depends(var)
@data == var
end
end
TRIVIALS =
{
[:x, :*, 1] => :x,
[:x, :+, 0] => :x,
[:x, :/, 1] => :x,
[:x, :-, 0] => :x,
[:x, :^, 1] => :x,
[:x, :^, 0] => 0,
[:x, :*, 0] => 0,
[:x, :/, 0] => Float::INFINITY,
[0, :^, :x] => 0,
[0, :/, :x] => 0,
[:x, :*, Float::INFINITY] => Float::INFINITY,
[:x, :+, Float::INFINITY] => Float::INFINITY,
[:x, :/, Float::INFINITY] => 0,
[:x, :-, Float::INFINITY] => Float::INFINITY,
[:x, :^, Float::INFINITY] => Float::INFINITY,
[Float::INFINITY, :/, :x] => Float::INFINITY,
[Float::INFINITY, :-, :x] => Float::INFINITY,
[Float::INFINITY, :^, :x] => Float::INFINITY,
}
class OpBinary < Operation
def evaluate vars={}
@args[0].evaluate(vars).send(@data == :^ ? :** : @data, @args[1].evaluate(vars))rescue @args[0].evaluate(vars).to_f.send(@data == :^ ? :** : @data, @args[1].evaluate(vars).to_f)
end
def to_s
"(#{@args[0]} #{@data} #{@args[1]})"
end
def priority
$registered_operators[@data]
end
def simplify
super
#evaluate constant expressions
return make_op(evaluate) if @args.all?{|x| x.is_a? OpConstant}
#simplify (a op a) cases
if @args[0].to_s == @args[1].to_s
case @data
when :-
return make_op(0)
when :+
return make_op(2, :*, @args[0])
when :*
return make_op(@args[0], :^, 2)
when :/
return make_op(1)
when :^
return self
end
end
#simplify trivial expressions
result = nil
TRIVIALS.each do |x, val|
first = x[0]
operator = x[1]
second = x[2]
next unless operator == @data
if $commutative[@data] && @args[0].is_a?(OpConstant)
@args[1],@args[0] = @args[0],@args[1]
end
if first == :x
it = @args[0]
else
next unless @args[0].is_a?(OpConstant) && @args[0].data == first
end
if second == :x
it = @args[1]
else
next unless @args[1].is_a?(OpConstant) && @args[1].data == second
end
result = (val == :x) ? it : make_op(val)
break
end
return result if result
#order for nice reading
if($commutative[@data] && @args[1].is_a?(OpConstant))
if (@args[1].evaluate < 0) || @data == :*
@args[1],@args[0] = @args[0],@args[1]
end
end
self
end
def differentiate(var)
u = @args[0].clone
v = @args[1].clone
du = @args[0].differentiate(var)
dv = @args[1].differentiate(var)
case @data
when :+, :-
make_op(du, @data, dv)
when :*
make_op( make_op(u, :*, dv), :+, make_op(v, :*, du) )
when :/
make_op( make_op( make_op(du, :*, v), :-, make_op(dv, :*, u) ), :/,make_op(v, :*, v))
when :^
if not u.depends(var)
#u^x
lnu = make_op(:log, u)
make_op(dv, :*, make_op(clone, :*, lnu))
elsif not v.depends(var)
#x^v
make_op(du, :*, make_op(v, :*, make_op(u, :^, make_op(v, :-, 1))))
else
#general case
lnu = make_op(:log, u)
make_op( make_op(u, :^, v), :*, make_op(make_op(dv, :*, lnu), :+, make_op(make_op(v, :*, du), :/, u)))
end
else
clone
end
end
end
class OpFunction < Operation
def evaluate vars={}
Math.send(@data, @args[0].evaluate(vars))
end
def simplify
super
return make_op(evaluate) if @args[0].is_a?(OpConstant)
self
end
def to_s
"#{@data.to_s}#{@args[0]}"
end
def differentiate(var)
y = @args[0]
dy = @args[0].differentiate(var)
df = case @data
when :sin
make_op(:cos,y)
when :cos
make_op(0, :-,make_op(:sin,y))
when :exp
clone
when :log
make_op(1, :/, y)
else
clone
end
make_op(dy, :*, df)
end
end
def parse lexems
expect_bin = false
cur_op = OpDummy.new(nil, nil)
nested = []
lexems.each do |lexem|
#p "cur=#{cur_op} parent=#{cur_op ? cur_op.parent : nil}"
#p "lexem=#{lexem.data} mode=#{expect_bin ? "bin" : "un"}"
# cur_op = cur_op.parent
if expect_bin
expect_bin = false
case lexem.typ
when :close
#cur_op = cur_op.parent
cur_op = nested.pop
expect_bin = true
when :operator
sym = lexem.data.to_sym
prio = $registered_operators[sym]
while cur_op.priority >= prio
cur_op = cur_op.parent
end
it = cur_op.args.last
op = OpBinary.new(cur_op, sym)
it.move_to(op)
cur_op = op
else
throw "unexpected token: #{lexem}"
end
else
case lexem.typ
when :open
nested.push(cur_op)
cur_op = OpDummy.new(cur_op, nil)
when :operator
throw "unexpected token: #{lexem}" unless $unary[lexem.data.to_sym]
cur_op = OpBinary.new(cur_op, lexem.data.to_sym)
OpConstant.new(cur_op, 0)
when :number
expect_bin = true
OpConstant.new(cur_op, lexem.data.to_r)
when :function
cur_op = OpFunction.new(cur_op, lexem.data.to_sym)
when :var
expect_bin = true
OpVariable.new(cur_op, lexem.data.to_sym)
else
throw "unexpected token: #{lexem}"
end
end
end
# p "cur=#{cur_op} parent=#{cur_op ? cur_op.parent : nil}"
while cur_op.parent
cur_op = cur_op.parent
end
cur_op.simplify
end
def test(str, var)
y = parse(lexer(str))
dy = y.differentiate(var).simplify
p("d#{y}/d#{var} = #{dy}")
end
test("-x6/1e-6+6*x6-1/x5", :x6)
test("-x6/1e-6+6*x6-1/x5", :x5)
test("-x/1+x/(1/6)", :x)
test("-13.5*(x*(-13))", :x)
test("-1*x+2", :x)
test("(1+1)", :x)
test("-(1/x)+2/x", :x)
test("-1/x+2/0", :x)
test("sin(x)", :x)
test("-(2*x)-3", :x)
test("sin(2*x)/x", :x)
test("x^x", :x)
test("x^x", :y)
test("x^y", :x)
test("y^x", :x)
test("(x-1)*(x+1)", :x)
test("x*x-1", :x)
test("2^exp(x)", :x)
test("(x*x)^0", :x)
test("x*(1/2-1/3-1/6)", :x)
test("0/0+x", :x)
I0FMTF9MRVhFTVMgPSBbOm9wZW4sIDpjbG9zZSwgOm9wZXJhdG9yLCA6bnVtYmVyLCA6ZnVuY3Rpb24sIDp2YXJdCgpJREVOVElGSUVSID0gL1thLXpBLVpdXHcqLwoKJHJlZ2lzdGVyZWRfZnVuY3Rpb25zID0ge3NpbjogMSwgY29zOiAxLCBsb2c6IDEsIGV4cDogMX0KJHJlZ2lzdGVyZWRfb3BlcmF0b3JzID0gezorID0+IDEsIDotID0+IDEsIDoqID0+IDIsIDovID0+IDIsIDpeID0+IDN9CiR1bmFyeSA9IHs6LSA9PiB0cnVlLCA6KyA9PiB0cnVlfQokY29tbXV0YXRpdmUgPSB7OisgPT4gdHJ1ZSwgOiogPT4gdHJ1ZX0KCmRlZiBudW1lcmljPyhvYmplY3QpCiAgdHJ1ZSBpZiBGbG9hdChvYmplY3QpIHJlc2N1ZSBmYWxzZQplbmQKCmNsYXNzIFRva2VuCiAgYXR0cl9hY2Nlc3NvciA6dHlwCiAgYXR0cl9hY2Nlc3NvciA6ZGF0YQoKICBkZWYgZGV0ZWN0X3R5cGUgcwogICAgQGRhdGEgPSBzCiAgICByZXR1cm4gOm9wZW4gaWYgcz09IigiCiAgICByZXR1cm4gOmNsb3NlIGlmIHM9PSIpIgogICAgcmV0dXJuIDpudW1iZXIgaWYgbnVtZXJpYz8ocykKICAgIHJldHVybiA6ZnVuY3Rpb24gaWYgJHJlZ2lzdGVyZWRfZnVuY3Rpb25zW3MudG9fc3ltXQogICAgcmV0dXJuIDpvcGVyYXRvciBpZiAkcmVnaXN0ZXJlZF9vcGVyYXRvcnNbcy50b19zeW1dCiAgICByZXR1cm4gOnZhciBpZiBJREVOVElGSUVSID1+IHMKICAgIHRocm93ICJ1bnJlY29nbmlzZWQgdG9rZW46ICN7c30iCiAgZW5kCgogIGRlZiBpbml0aWFsaXplIHMKICAgIEB0eXAgPSBkZXRlY3RfdHlwZShzKQogIGVuZAoKICBkZWYgdG9fcwogICAgIlsje0B0eXB9OiAje0BkYXRhfV0iCiAgZW5kCgplbmQKCmRlZiBsZXhlciBzdHJpbmcKICBzdHJpbmcuc2NhbigvKD86WzAtOV0qXC4/WzAtOV0rKD86W2VFXVstK10/WzAtOV0rKT8pfFthLXpBLVpdXHcqfFxTLykubWFwe3x4fCBUb2tlbi5uZXcoeCl9CmVuZAoKY2xhc3MgT3BlcmF0aW9uCiAgYXR0cl9hY2Nlc3NvciA6YXJncywgOnBhcmVudCwgOmRhdGEKCiAgZGVmIGV2YWx1YXRlIHZhcnM9e30KICAgIHRocm93IDphYnN0cmFjdAogIGVuZAogIGRlZiBzaW1wbGlmeQojICAgIHAgInNpbXBsaWZ5OiAje3NlbGZ9IgogICAgQGFyZ3MubWFwISgmOnNpbXBsaWZ5KQogICAgc2VsZgogIGVuZAogIGRlZiBpbml0aWFsaXplIHBhcmVudCxkYXRhCiAgICBAZGF0YSA9IGRhdGEKICAgIEBhcmdzID0gW10KICAgIG1vdmVfdG8ocGFyZW50KQogIGVuZAogIGRlZiBtb3ZlX3RvIHBhcmVudAogICAgQHBhcmVudC5hcmdzLmRlbGV0ZShzZWxmKSBpZiBAcGFyZW50CiAgICBAcGFyZW50ID0gcGFyZW50CiAgICBAcGFyZW50LmFyZ3MgPDwgc2VsZiBpZiBAcGFyZW50CiAgZW5kCiAgZGVmIHRvX3MKICAgIEBkYXRhLnRvX3MKICBlbmQKICBkZWYgcHJpb3JpdHkKICAgIDEwMDAwCiAgZW5kCiAgZGVmIGRpZmZlcmVudGlhdGUodmFyKQogICAgdGhyb3cgOmFic3RyYWN0CiAgZW5kCiAgZGVmIGRlcGVuZHModmFyKQogICAgQGFyZ3MuYW55P3t8YXwgYS5kZXBlbmRzKHZhcil9CiAgZW5kCmVuZAoKI3N5bnRheCBzdWdhcgpkZWYgbWFrZV9vcCgqcHNldWRvKQojICBwIHBzZXVkbwogIGNhc2UgcHNldWRvLmxlbmd0aAogICAgd2hlbiAxCiAgICAgIGlmIHBzZXVkby5maXJzdC5pc19hPyhTeW1ib2wpCiAgICAgICAgcmVzdWx0ID0gT3BWYXJpYWJsZS5uZXcobmlsLCBwc2V1ZG8uZmlyc3QpCiAgICAgIGVsc2lmIHBzZXVkby5maXJzdC5pc19hPyhPcGVyYXRpb24pCiAgICAgICAgcmVzdWx0ID0gcHNldWRvLmZpcnN0CiAgICAgIGVsc2UKICAgICAgICByZXN1bHQgPSBPcENvbnN0YW50Lm5ldyhuaWwsIHBzZXVkby5maXJzdCkKICAgICAgZW5kCiAgICB3aGVuIDIuLjMKICAgICAgaWYgJHJlZ2lzdGVyZWRfZnVuY3Rpb25zW3BzZXVkb1swXV0KICAgICAgICByZXN1bHQgPSBPcEZ1bmN0aW9uLm5ldyhuaWwsIHBzZXVkb1swXSkKICAgICAgICByZXN1bHQuYXJncyA9IFttYWtlX29wKHBzZXVkb1sxXSldCiAgICAgICAgcmVzdWx0CiAgICAgIGVsc2UKICAgICAgICByZXN1bHQgPSBPcEJpbmFyeS5uZXcobmlsLCBwc2V1ZG9bMV0pCiAgICAgICAgbnVtcyA9IHBzZXVkby5sZW5ndGggPiAyID8gWzAsMl0gOiBbMF0KICAgICAgICByZXN1bHQuYXJncyA9IG51bXMubWFwe3x4fCBtYWtlX29wKHBzZXVkb1t4XSl9CiAgICAgICAgcmVzdWx0CiAgICAgIGVuZAogIGVuZAplbmQKCgpjbGFzcyBPcENvbnN0YW50IDwgT3BlcmF0aW9uCiAgZGVmIGV2YWx1YXRlIHZhcnM9e30KICAgIEBkYXRhCiAgZW5kCiAgZGVmIGRpZmZlcmVudGlhdGUodmFyKQogICAgbWFrZV9vcCgwKQogIGVuZAogIGRlZiB0b19zCiAgICBAZGF0YS50b19mLnRvX3MKICBlbmQKZW5kCgpjbGFzcyBPcER1bW15IDwgT3BlcmF0aW9uCiAgZGVmIGV2YWx1YXRlIHZhcnM9e30KICAgIEBhcmdzWzBdLmV2YWx1YXRlKHZhcnMpCiAgZW5kCiAgZGVmIGRpZmZlcmVudGlhdGUodmFyKQogICAgQGFyZ3NbMF0uZGlmZmVyZW50aWF0ZSh2YXIpCiAgZW5kCiAgZGVmIHNpbXBsaWZ5CiAgICBzdXBlcgogICAgQGFyZ3NbMF0KICBlbmQKICBkZWYgdG9fcwogICAgIigje0BhcmdzWzBdfSkiCiAgZW5kCiAgZGVmIHByaW9yaXR5CiAgICAwCiAgZW5kCmVuZAoKY2xhc3MgT3BWYXJpYWJsZSA8IE9wZXJhdGlvbgogIGRlZiBldmFsdWF0ZSB2YXJzPXt9CiAgICB2YXJzW0BkYXRhXQogIGVuZAogIGRlZiBkaWZmZXJlbnRpYXRlKHZhcikKICAgIG1ha2Vfb3AoQGRhdGEgPT0gdmFyID8gMSA6IDApCiAgZW5kCiAgZGVmIGRlcGVuZHModmFyKQogICAgQGRhdGEgPT0gdmFyCiAgZW5kCmVuZAoKVFJJVklBTFMgPQogICAgewogICAgICAgIFs6eCwgOiosIDFdID0+IDp4LAogICAgICAgIFs6eCwgOissIDBdID0+IDp4LAogICAgICAgIFs6eCwgOi8sIDFdID0+IDp4LAogICAgICAgIFs6eCwgOi0sIDBdID0+IDp4LAogICAgICAgIFs6eCwgOl4sIDFdID0+IDp4LAoKICAgICAgICBbOngsIDpeLCAwXSA9PiAwLAogICAgICAgIFs6eCwgOiosIDBdID0+IDAsCiAgICAgICAgWzp4LCA6LywgMF0gPT4gRmxvYXQ6OklORklOSVRZLAogICAgICAgIFswLCA6XiwgOnhdID0+IDAsCiAgICAgICAgWzAsIDovLCA6eF0gPT4gMCwKCiAgICAgICAgWzp4LCA6KiwgRmxvYXQ6OklORklOSVRZXSA9PiBGbG9hdDo6SU5GSU5JVFksCiAgICAgICAgWzp4LCA6KywgRmxvYXQ6OklORklOSVRZXSA9PiBGbG9hdDo6SU5GSU5JVFksCiAgICAgICAgWzp4LCA6LywgRmxvYXQ6OklORklOSVRZXSA9PiAwLAogICAgICAgIFs6eCwgOi0sIEZsb2F0OjpJTkZJTklUWV0gPT4gRmxvYXQ6OklORklOSVRZLAogICAgICAgIFs6eCwgOl4sIEZsb2F0OjpJTkZJTklUWV0gPT4gRmxvYXQ6OklORklOSVRZLAogICAgICAgIFtGbG9hdDo6SU5GSU5JVFksIDovLCA6eF0gPT4gRmxvYXQ6OklORklOSVRZLAogICAgICAgIFtGbG9hdDo6SU5GSU5JVFksIDotLCA6eF0gPT4gRmxvYXQ6OklORklOSVRZLAogICAgICAgIFtGbG9hdDo6SU5GSU5JVFksIDpeLCA6eF0gPT4gRmxvYXQ6OklORklOSVRZLAogICAgfQoKY2xhc3MgT3BCaW5hcnkgPCBPcGVyYXRpb24KICBkZWYgZXZhbHVhdGUgdmFycz17fQogICAgQGFyZ3NbMF0uZXZhbHVhdGUodmFycykuc2VuZChAZGF0YSA9PSA6XiA/IDoqKiA6IEBkYXRhLCBAYXJnc1sxXS5ldmFsdWF0ZSh2YXJzKSlyZXNjdWUgQGFyZ3NbMF0uZXZhbHVhdGUodmFycykudG9fZi5zZW5kKEBkYXRhID09IDpeID8gOioqIDogQGRhdGEsIEBhcmdzWzFdLmV2YWx1YXRlKHZhcnMpLnRvX2YpCiAgZW5kCgogIGRlZiB0b19zCiAgICAiKCN7QGFyZ3NbMF19ICN7QGRhdGF9ICN7QGFyZ3NbMV19KSIKICBlbmQKICBkZWYgcHJpb3JpdHkKICAgICRyZWdpc3RlcmVkX29wZXJhdG9yc1tAZGF0YV0KICBlbmQKICBkZWYgc2ltcGxpZnkKICAgIHN1cGVyCiAgICAjZXZhbHVhdGUgY29uc3RhbnQgZXhwcmVzc2lvbnMKICAgIHJldHVybiBtYWtlX29wKGV2YWx1YXRlKSBpZiBAYXJncy5hbGw/e3x4fCB4LmlzX2E/IE9wQ29uc3RhbnR9CgogICAgI3NpbXBsaWZ5IChhIG9wIGEpIGNhc2VzCiAgICBpZiBAYXJnc1swXS50b19zID09IEBhcmdzWzFdLnRvX3MKICAgICAgY2FzZSBAZGF0YQogICAgICAgIHdoZW4gOi0KICAgICAgICAgIHJldHVybiBtYWtlX29wKDApCiAgICAgICAgd2hlbiA6KwogICAgICAgICAgcmV0dXJuIG1ha2Vfb3AoMiwgOiosIEBhcmdzWzBdKQogICAgICAgIHdoZW4gOioKICAgICAgICAgIHJldHVybiBtYWtlX29wKEBhcmdzWzBdLCA6XiwgMikKICAgICAgICB3aGVuIDovCiAgICAgICAgICByZXR1cm4gbWFrZV9vcCgxKQogICAgICAgIHdoZW4gOl4KICAgICAgICAgIHJldHVybiBzZWxmCiAgICAgIGVuZAogICAgZW5kCgogICAgI3NpbXBsaWZ5IHRyaXZpYWwgZXhwcmVzc2lvbnMKICAgIHJlc3VsdCA9IG5pbAogICAgVFJJVklBTFMuZWFjaCBkbyB8eCwgdmFsfAogICAgICBmaXJzdCA9IHhbMF0KICAgICAgb3BlcmF0b3IgPSB4WzFdCiAgICAgIHNlY29uZCA9IHhbMl0KICAgICAgbmV4dCB1bmxlc3Mgb3BlcmF0b3IgPT0gQGRhdGEKICAgICAgaWYgJGNvbW11dGF0aXZlW0BkYXRhXSAmJiBAYXJnc1swXS5pc19hPyhPcENvbnN0YW50KQogICAgICAgIEBhcmdzWzFdLEBhcmdzWzBdID0gQGFyZ3NbMF0sQGFyZ3NbMV0KICAgICAgZW5kCiAgICAgIGlmIGZpcnN0ID09IDp4CiAgICAgICAgaXQgPSBAYXJnc1swXQogICAgICBlbHNlCiAgICAgICAgbmV4dCB1bmxlc3MgQGFyZ3NbMF0uaXNfYT8oT3BDb25zdGFudCkgJiYgQGFyZ3NbMF0uZGF0YSA9PSBmaXJzdAogICAgICBlbmQKICAgICAgaWYgc2Vjb25kID09IDp4CiAgICAgICAgaXQgPSBAYXJnc1sxXQogICAgICBlbHNlCiAgICAgICAgbmV4dCB1bmxlc3MgQGFyZ3NbMV0uaXNfYT8oT3BDb25zdGFudCkgJiYgQGFyZ3NbMV0uZGF0YSA9PSBzZWNvbmQKICAgICAgZW5kCiAgICAgIHJlc3VsdCA9ICh2YWwgPT0gOngpID8gaXQgOiBtYWtlX29wKHZhbCkKICAgICAgYnJlYWsKICAgIGVuZAogICAgcmV0dXJuIHJlc3VsdCBpZiByZXN1bHQKCiAgICAjb3JkZXIgZm9yIG5pY2UgcmVhZGluZwogICAgaWYoJGNvbW11dGF0aXZlW0BkYXRhXSAmJiBAYXJnc1sxXS5pc19hPyhPcENvbnN0YW50KSkKICAgICAgaWYgKEBhcmdzWzFdLmV2YWx1YXRlIDwgMCkgfHwgQGRhdGEgPT0gOioKICAgICAgICBAYXJnc1sxXSxAYXJnc1swXSA9IEBhcmdzWzBdLEBhcmdzWzFdCiAgICAgIGVuZAogICAgZW5kCiAgICBzZWxmCiAgZW5kCgoKICBkZWYgZGlmZmVyZW50aWF0ZSh2YXIpCiAgICB1ID0gQGFyZ3NbMF0uY2xvbmUKICAgIHYgPSBAYXJnc1sxXS5jbG9uZQogICAgZHUgPSBAYXJnc1swXS5kaWZmZXJlbnRpYXRlKHZhcikKICAgIGR2ID0gQGFyZ3NbMV0uZGlmZmVyZW50aWF0ZSh2YXIpCiAgICBjYXNlIEBkYXRhCiAgICAgIHdoZW4gOissIDotCiAgICAgICAgbWFrZV9vcChkdSwgQGRhdGEsIGR2KQogICAgICB3aGVuIDoqCiAgICAgICAgbWFrZV9vcCggbWFrZV9vcCh1LCA6KiwgZHYpLCA6KywgbWFrZV9vcCh2LCA6KiwgZHUpICkKICAgICAgd2hlbiA6LwogICAgICAgIG1ha2Vfb3AoIG1ha2Vfb3AoIG1ha2Vfb3AoZHUsIDoqLCB2KSwgOi0sIG1ha2Vfb3AoZHYsIDoqLCB1KSApLCA6LyxtYWtlX29wKHYsIDoqLCB2KSkKICAgICAgd2hlbiA6XgogICAgICAgIGlmIG5vdCB1LmRlcGVuZHModmFyKQogICAgICAgICAgI3VeeAogICAgICAgICAgbG51ID0gbWFrZV9vcCg6bG9nLCB1KQogICAgICAgICAgbWFrZV9vcChkdiwgOiosIG1ha2Vfb3AoY2xvbmUsIDoqLCBsbnUpKQogICAgICAgIGVsc2lmIG5vdCB2LmRlcGVuZHModmFyKQogICAgICAgICAgI3hedgogICAgICAgICAgbWFrZV9vcChkdSwgOiosIG1ha2Vfb3AodiwgOiosIG1ha2Vfb3AodSwgOl4sIG1ha2Vfb3AodiwgOi0sIDEpKSkpCiAgICAgICAgZWxzZQogICAgICAgICAgI2dlbmVyYWwgY2FzZQogICAgICAgICAgbG51ID0gbWFrZV9vcCg6bG9nLCB1KQogICAgICAgICAgbWFrZV9vcCggbWFrZV9vcCh1LCA6XiwgdiksIDoqLCBtYWtlX29wKG1ha2Vfb3AoZHYsIDoqLCBsbnUpLCA6KywgbWFrZV9vcChtYWtlX29wKHYsIDoqLCBkdSksIDovLCB1KSkpCiAgICAgICAgZW5kCiAgICAgIGVsc2UKICAgICAgICBjbG9uZQogICAgZW5kCiAgZW5kCgplbmQKCmNsYXNzIE9wRnVuY3Rpb24gPCBPcGVyYXRpb24KICBkZWYgZXZhbHVhdGUgdmFycz17fQogICAgTWF0aC5zZW5kKEBkYXRhLCBAYXJnc1swXS5ldmFsdWF0ZSh2YXJzKSkKICBlbmQKICBkZWYgc2ltcGxpZnkKICAgIHN1cGVyCiAgICByZXR1cm4gbWFrZV9vcChldmFsdWF0ZSkgaWYgQGFyZ3NbMF0uaXNfYT8oT3BDb25zdGFudCkKICAgIHNlbGYKICBlbmQKICBkZWYgdG9fcwogICAgIiN7QGRhdGEudG9fc30je0BhcmdzWzBdfSIKICBlbmQKICBkZWYgZGlmZmVyZW50aWF0ZSh2YXIpCiAgICB5ID0gQGFyZ3NbMF0KICAgIGR5ID0gQGFyZ3NbMF0uZGlmZmVyZW50aWF0ZSh2YXIpCiAgICBkZiA9IGNhc2UgQGRhdGEKICAgICAgICAgIHdoZW4gOnNpbgogICAgICAgICAgICBtYWtlX29wKDpjb3MseSkKICAgICAgICAgIHdoZW4gOmNvcwogICAgICAgICAgICBtYWtlX29wKDAsIDotLG1ha2Vfb3AoOnNpbix5KSkKICAgICAgICAgIHdoZW4gOmV4cAogICAgICAgICAgICBjbG9uZQogICAgICAgICAgIHdoZW4gOmxvZwogICAgICAgICAgICAgbWFrZV9vcCgxLCA6LywgeSkKICAgICAgICAgIGVsc2UKICAgICAgICAgICAgY2xvbmUKICAgICAgICAgZW5kCiAgICBtYWtlX29wKGR5LCA6KiwgZGYpCiAgZW5kCmVuZAoKCgpkZWYgcGFyc2UgbGV4ZW1zCiAgZXhwZWN0X2JpbiA9IGZhbHNlCiAgY3VyX29wID0gT3BEdW1teS5uZXcobmlsLCBuaWwpCiAgbmVzdGVkID0gW10KICBsZXhlbXMuZWFjaCBkbyB8bGV4ZW18CiAgICAjcCAiY3VyPSN7Y3VyX29wfSBwYXJlbnQ9I3tjdXJfb3AgPyBjdXJfb3AucGFyZW50IDogbmlsfSIKICAgICNwICJsZXhlbT0je2xleGVtLmRhdGF9IG1vZGU9I3tleHBlY3RfYmluID8gImJpbiIgOiAidW4ifSIKICAgICAgIyBjdXJfb3AgPSBjdXJfb3AucGFyZW50CiAgICBpZiBleHBlY3RfYmluCiAgICAgIGV4cGVjdF9iaW4gPSBmYWxzZQogICAgICBjYXNlIGxleGVtLnR5cAogICAgICAgIHdoZW4gOmNsb3NlCiAgICAgICAgICAjY3VyX29wID0gY3VyX29wLnBhcmVudAogICAgICAgICAgY3VyX29wID0gbmVzdGVkLnBvcAogICAgICAgICAgZXhwZWN0X2JpbiA9IHRydWUKICAgICAgICB3aGVuIDpvcGVyYXRvcgogICAgICAgICAgc3ltID0gbGV4ZW0uZGF0YS50b19zeW0KICAgICAgICAgIHByaW8gPSAkcmVnaXN0ZXJlZF9vcGVyYXRvcnNbc3ltXQogICAgICAgICAgd2hpbGUgY3VyX29wLnByaW9yaXR5ID49IHByaW8KICAgICAgICAgICAgY3VyX29wID0gY3VyX29wLnBhcmVudAogICAgICAgICAgZW5kCiAgICAgICAgICBpdCA9IGN1cl9vcC5hcmdzLmxhc3QKICAgICAgICAgIG9wID0gT3BCaW5hcnkubmV3KGN1cl9vcCwgc3ltKQogICAgICAgICAgaXQubW92ZV90byhvcCkKICAgICAgICAgIGN1cl9vcCA9IG9wCiAgICAgICAgZWxzZQogICAgICAgICAgdGhyb3cgInVuZXhwZWN0ZWQgdG9rZW46ICN7bGV4ZW19IgogICAgICBlbmQKICAgIGVsc2UKICAgICAgY2FzZSBsZXhlbS50eXAKICAgICAgICB3aGVuIDpvcGVuCiAgICAgICAgICBuZXN0ZWQucHVzaChjdXJfb3ApCiAgICAgICAgICBjdXJfb3AgPSBPcER1bW15Lm5ldyhjdXJfb3AsIG5pbCkKICAgICAgICB3aGVuIDpvcGVyYXRvcgogICAgICAgICAgdGhyb3cgInVuZXhwZWN0ZWQgdG9rZW46ICN7bGV4ZW19IiB1bmxlc3MgJHVuYXJ5W2xleGVtLmRhdGEudG9fc3ltXQogICAgICAgICAgY3VyX29wID0gT3BCaW5hcnkubmV3KGN1cl9vcCwgbGV4ZW0uZGF0YS50b19zeW0pCiAgICAgICAgICBPcENvbnN0YW50Lm5ldyhjdXJfb3AsIDApCiAgICAgICAgd2hlbiA6bnVtYmVyCiAgICAgICAgICBleHBlY3RfYmluID0gdHJ1ZQogICAgICAgICAgT3BDb25zdGFudC5uZXcoY3VyX29wLCBsZXhlbS5kYXRhLnRvX3IpCiAgICAgICAgd2hlbiA6ZnVuY3Rpb24KICAgICAgICAgIGN1cl9vcCA9IE9wRnVuY3Rpb24ubmV3KGN1cl9vcCwgbGV4ZW0uZGF0YS50b19zeW0pCiAgICAgICAgd2hlbiA6dmFyCiAgICAgICAgICBleHBlY3RfYmluID0gdHJ1ZQogICAgICAgICAgT3BWYXJpYWJsZS5uZXcoY3VyX29wLCBsZXhlbS5kYXRhLnRvX3N5bSkKICAgICAgICBlbHNlCiAgICAgICAgICB0aHJvdyAidW5leHBlY3RlZCB0b2tlbjogI3tsZXhlbX0iCiAgICAgIGVuZAogICAgZW5kCiAgZW5kCiMgIHAgImN1cj0je2N1cl9vcH0gcGFyZW50PSN7Y3VyX29wID8gY3VyX29wLnBhcmVudCA6IG5pbH0iCiAgd2hpbGUgY3VyX29wLnBhcmVudAogICAgY3VyX29wID0gY3VyX29wLnBhcmVudAogIGVuZAogIGN1cl9vcC5zaW1wbGlmeQplbmQKCmRlZiB0ZXN0KHN0ciwgdmFyKQogIHkgPSBwYXJzZShsZXhlcihzdHIpKQogIGR5ID0geS5kaWZmZXJlbnRpYXRlKHZhcikuc2ltcGxpZnkKICBwKCJkI3t5fS9kI3t2YXJ9ID0gI3tkeX0iKQplbmQKdGVzdCgiLXg2LzFlLTYrNip4Ni0xL3g1IiwgOng2KQp0ZXN0KCIteDYvMWUtNis2Kng2LTEveDUiLCA6eDUpCnRlc3QoIi14LzEreC8oMS82KSIsIDp4KQp0ZXN0KCItMTMuNSooeCooLTEzKSkiLCA6eCkKCnRlc3QoIi0xKngrMiIsIDp4KQp0ZXN0KCIoMSsxKSIsIDp4KQp0ZXN0KCItKDEveCkrMi94IiwgOngpCnRlc3QoIi0xL3grMi8wIiwgOngpCgp0ZXN0KCJzaW4oeCkiLCA6eCkKdGVzdCgiLSgyKngpLTMiLCA6eCkKdGVzdCgic2luKDIqeCkveCIsIDp4KQoKdGVzdCgieF54IiwgOngpCnRlc3QoInheeCIsIDp5KQp0ZXN0KCJ4XnkiLCA6eCkKdGVzdCgieV54IiwgOngpCgp0ZXN0KCIoeC0xKSooeCsxKSIsIDp4KQp0ZXN0KCJ4KngtMSIsIDp4KQp0ZXN0KCIyXmV4cCh4KSIsIDp4KQp0ZXN0KCIoeCp4KV4wIiwgOngpCnRlc3QoIngqKDEvMi0xLzMtMS82KSIsIDp4KQoKdGVzdCgiMC8wK3giLCA6eCkK