2005-05-09 11:52:26 +00:00
|
|
|
# $Dwarf: intspan.rb,v 1.20 2005/03/11 12:17:35 ward Exp $
|
2002-04-27 20:34:15 +00:00
|
|
|
# $Source$
|
2003-07-20 20:32:24 +00:00
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Copyright (c) 2002, 2003 Ward Wouts <ward@wouts.nl>
|
2002-04-27 20:34:15 +00:00
|
|
|
#
|
2003-07-20 20:32:24 +00:00
|
|
|
# Permission to use, copy, modify, and distribute this software for any
|
|
|
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
|
|
|
# copyright notice and this permission notice appear in all copies.
|
2002-04-27 20:31:59 +00:00
|
|
|
#
|
2003-07-20 20:32:24 +00:00
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
2002-04-27 20:31:59 +00:00
|
|
|
#
|
|
|
|
|
|
2005-03-09 18:14:56 +00:00
|
|
|
class Integer
|
|
|
|
|
def even?
|
|
|
|
|
return self%2 == 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def odd?
|
|
|
|
|
return self%2 == 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2002-04-27 20:31:59 +00:00
|
|
|
module Set
|
|
|
|
|
|
|
|
|
|
class IntSpan
|
|
|
|
|
|
2005-03-09 14:22:52 +00:00
|
|
|
Empty_String = ''
|
2002-04-27 20:31:59 +00:00
|
|
|
Debuglevel = 0
|
|
|
|
|
|
|
|
|
|
def initialize(setspec=nil)
|
|
|
|
|
print "initialize: Calling copy\n" if Debuglevel > 0
|
|
|
|
|
copy(setspec)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def IntSpan.valid(run_list)
|
|
|
|
|
testset = new
|
|
|
|
|
begin
|
|
|
|
|
testset._copy_run_list(run_list)
|
|
|
|
|
rescue SystemExit
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def copy(set_spec)
|
2004-06-16 08:17:48 +00:00
|
|
|
print "Copy #{set_spec.class.to_s}\n" if Debuglevel > 0
|
|
|
|
|
case set_spec.class.to_s
|
2002-04-27 20:31:59 +00:00
|
|
|
when "NilClass"
|
|
|
|
|
print "copy: Calling _copy_empty\n" if Debuglevel > 0
|
|
|
|
|
_copy_empty
|
|
|
|
|
when "String"
|
|
|
|
|
print "copy: Calling _copy_run_list\n" if Debuglevel > 0
|
|
|
|
|
_copy_run_list(set_spec)
|
|
|
|
|
when "Array"
|
|
|
|
|
print "copy: Calling _copy_array\n" if Debuglevel > 0
|
|
|
|
|
_copy_array(set_spec)
|
2005-03-09 15:02:21 +00:00
|
|
|
when "Set::IntSpan"
|
|
|
|
|
print "copy: Calling _copy_set\n" if Debuglevel > 0
|
2002-04-27 20:31:59 +00:00
|
|
|
_copy_set(set_spec)
|
|
|
|
|
else
|
|
|
|
|
print "eeps\n"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
def _copy_empty # makes @set the empty set
|
|
|
|
|
@set = {}
|
|
|
|
|
set_neg_inf(false)
|
|
|
|
|
set_pos_inf(false)
|
2002-04-27 20:31:59 +00:00
|
|
|
@set["edges"] = []
|
|
|
|
|
@set["run"] = []
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def _copy_array(array) # copies an array into @set
|
2005-03-09 15:02:21 +00:00
|
|
|
_copy_empty
|
|
|
|
|
set_neg_inf(false)
|
|
|
|
|
set_pos_inf(false)
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
edges = []
|
|
|
|
|
for element in array.sort
|
2002-04-27 23:41:29 +00:00
|
|
|
next if (edges.length > 0) and (edges[-1] == element) # skip duplicates
|
2002-04-27 20:31:59 +00:00
|
|
|
|
2002-04-27 23:41:29 +00:00
|
|
|
if (edges.length > 0) and (edges[-1] == element-1)
|
|
|
|
|
edges[-1] = element
|
2002-04-27 20:31:59 +00:00
|
|
|
else
|
2002-08-01 09:22:29 +00:00
|
|
|
edges.push(element-1, element)
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@set["edges"] = edges
|
|
|
|
|
@set["run"] = []
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def _copy_set(src) # copies one set to another
|
2005-03-09 15:02:21 +00:00
|
|
|
_copy_empty
|
|
|
|
|
set_neg_inf(src.neg_inf?)
|
|
|
|
|
set_pos_inf(src.pos_inf?)
|
2002-04-27 20:31:59 +00:00
|
|
|
@set["edges"] = src.edges
|
|
|
|
|
@set["run"] = []
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def _copy_run_list(runlist)
|
|
|
|
|
_copy_empty
|
|
|
|
|
|
|
|
|
|
runlist.gsub!(/\s|_/, '')
|
|
|
|
|
return true if runlist == ""
|
|
|
|
|
|
|
|
|
|
print "copy run list...\n" if Debuglevel > 0
|
|
|
|
|
|
|
|
|
|
first = true
|
|
|
|
|
last = false
|
|
|
|
|
|
|
|
|
|
edges = []
|
|
|
|
|
|
|
|
|
|
for i in runlist.split(/,/)
|
|
|
|
|
print "#{i}\n" if Debuglevel > 0
|
|
|
|
|
begin
|
|
|
|
|
if i =~ /^(-?\d+)$/x
|
2002-08-01 09:22:29 +00:00
|
|
|
edges.push(($1.to_i-1), $1.to_i)
|
2002-04-27 20:31:59 +00:00
|
|
|
next
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if i =~ /^ (-?\d+) - (-?\d+) $/x
|
|
|
|
|
if $1.to_i > $2.to_i
|
|
|
|
|
print "match rule 1 #{$1} > #{$2}\n"
|
|
|
|
|
print "Set::IntSpan::_copy_run_list: Bad order: #{runlist}\n"
|
|
|
|
|
exit
|
|
|
|
|
else
|
2002-08-01 09:22:29 +00:00
|
|
|
edges.push(($1.to_i-1), $2.to_i)
|
2002-04-27 20:31:59 +00:00
|
|
|
next
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if i =~ /^\(-(-?\d+)$/x
|
|
|
|
|
unless first
|
|
|
|
|
print "match rule 2\n"
|
|
|
|
|
print "Set::IntSpan::_copy_run_list: Bad order: #{runlist}\n"
|
|
|
|
|
exit
|
|
|
|
|
end
|
2005-03-09 14:22:52 +00:00
|
|
|
set_neg_inf(true)
|
2002-08-01 09:22:29 +00:00
|
|
|
edges.push($1.to_i)
|
2002-04-27 20:31:59 +00:00
|
|
|
next
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if i =~ /^(-?\d+)-\)$/x
|
2005-03-09 14:22:52 +00:00
|
|
|
# print "match rule 3\n"
|
2002-08-01 09:22:29 +00:00
|
|
|
edges.push(($1.to_i-1))
|
2005-03-09 14:22:52 +00:00
|
|
|
set_pos_inf(true)
|
2002-04-27 20:31:59 +00:00
|
|
|
last = true
|
|
|
|
|
next
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if i =~ /^\(-\)$/x
|
|
|
|
|
unless first
|
|
|
|
|
print "match rule 4\n"
|
|
|
|
|
print "Set::IntSpan::_copy_run_list: Bad order: #{runlist}\n"
|
|
|
|
|
exit
|
|
|
|
|
end
|
2005-03-09 14:22:52 +00:00
|
|
|
set_neg_inf(true)
|
|
|
|
|
set_pos_inf(true)
|
2002-04-27 20:31:59 +00:00
|
|
|
last = true
|
|
|
|
|
next
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
print "no match! \"#{i}\"\n"
|
|
|
|
|
print "Set::IntSpan::_copy_run_list: Bad syntax: #{runlist}\n"
|
|
|
|
|
end
|
|
|
|
|
first = false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@set["edges"] = edges
|
|
|
|
|
@set["run"] = []
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
#def splice(array, offset, length=nil, list=[])
|
|
|
|
|
# if offset >= 0
|
|
|
|
|
# length = array.length-offset unless length
|
|
|
|
|
# leftarray = array.slice(0, offset)
|
|
|
|
|
# rightarray = array.slice(offset+length, (array.length - offset))
|
|
|
|
|
# else
|
|
|
|
|
# length = array.length+offset unless length
|
|
|
|
|
# leftarray = array.slice(0, (array.length+offset))
|
|
|
|
|
# rightarray = array.slice(array.length+length+offset, array.length+offset)
|
|
|
|
|
# end
|
|
|
|
|
#
|
|
|
|
|
# array = leftarray
|
|
|
|
|
# array += list
|
|
|
|
|
# array += rightarray if rightarray
|
|
|
|
|
#
|
|
|
|
|
# return array
|
|
|
|
|
#end
|
|
|
|
|
|
2005-03-09 14:22:52 +00:00
|
|
|
def to_s
|
|
|
|
|
return run_list
|
|
|
|
|
end
|
|
|
|
|
|
2002-04-27 20:31:59 +00:00
|
|
|
def run_list
|
2005-03-09 14:22:52 +00:00
|
|
|
if empty?
|
|
|
|
|
return Empty_String
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
print "edges leng: ", @set["edges"].length, "\n" if Debuglevel > 0
|
|
|
|
|
edges = []
|
2002-08-19 14:48:00 +00:00
|
|
|
edges.concat(@set["edges"])
|
2002-04-27 20:31:59 +00:00
|
|
|
runs = []
|
|
|
|
|
|
|
|
|
|
if edges.length > 0
|
2005-03-09 14:22:52 +00:00
|
|
|
edges.unshift('(') if neg_inf?
|
|
|
|
|
edges.push(')') if pos_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
print edges.join("/"),"\n" if Debuglevel > 0
|
|
|
|
|
|
2008-02-12 15:16:53 +00:00
|
|
|
i = 0
|
|
|
|
|
while i < edges.length
|
2002-04-27 20:31:59 +00:00
|
|
|
print "edges leng: ", @set["edges"].length, "\n" if Debuglevel > 0
|
2008-02-12 15:16:53 +00:00
|
|
|
lower = edges[i]
|
|
|
|
|
i += 1
|
|
|
|
|
upper = edges[i]
|
|
|
|
|
i += 1
|
2002-04-27 20:31:59 +00:00
|
|
|
print "Lower: \"#{lower}\" Upper: \"#{upper}\"\n" if Debuglevel > 0
|
2005-03-09 14:22:52 +00:00
|
|
|
if !(lower.to_s == '(') and !(upper.to_s == ')') and ((lower+1) == upper)
|
2002-04-27 20:31:59 +00:00
|
|
|
print "#{upper}\n" if Debuglevel > 0
|
2002-08-01 09:22:29 +00:00
|
|
|
runs.push("#{upper}")
|
2002-04-27 20:31:59 +00:00
|
|
|
else
|
|
|
|
|
lower += 1 if (lower.to_s <=> "(")!=0
|
|
|
|
|
print "#{lower}-#{upper}\n" if Debuglevel > 0
|
2002-08-01 09:22:29 +00:00
|
|
|
runs.push("#{lower}-#{upper}")
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
print "edges leng: ", @set["edges"].length, "\n" if Debuglevel > 0
|
|
|
|
|
|
|
|
|
|
return runs.join(',')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def elements
|
2005-03-09 18:14:56 +00:00
|
|
|
if infinite?
|
2002-04-27 20:31:59 +00:00
|
|
|
print "Set::IntSpan::elements: infinite set\n"
|
|
|
|
|
exit
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
elements = []
|
2002-04-28 15:06:02 +00:00
|
|
|
edges = @set["edges"].dup
|
2002-04-27 20:31:59 +00:00
|
|
|
while (edges.length>0)
|
|
|
|
|
lower, upper = edges.slice!(0..1)
|
|
|
|
|
elements += (lower+1 .. upper).to_a
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return elements
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def _real_set(set_spec=nil) # converts a set specification into a set
|
2004-06-16 08:17:48 +00:00
|
|
|
(set_spec != nil and set_spec.class.to_s == "Set::IntSpan") ?
|
2002-04-27 20:31:59 +00:00
|
|
|
set_spec :
|
|
|
|
|
IntSpan.new(set_spec)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def union(set_spec)
|
|
|
|
|
b = _real_set(set_spec)
|
|
|
|
|
s = IntSpan.new
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
s.set_neg_inf(neg_inf? || b.neg_inf?)
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
eA = @set["edges"]
|
|
|
|
|
eB = b.edges
|
|
|
|
|
eS = s.edges
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
inA = neg_inf?
|
2005-03-09 14:22:52 +00:00
|
|
|
inB = b.neg_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
iA = 0
|
|
|
|
|
iB = 0
|
|
|
|
|
|
|
|
|
|
while (iA < eA.length and iB < eB.length)
|
|
|
|
|
xA = eA[iA]
|
|
|
|
|
xB = eB[iB]
|
|
|
|
|
|
|
|
|
|
if (xA < xB)
|
|
|
|
|
iA += 1
|
|
|
|
|
inA = ! inA
|
2002-08-01 09:22:29 +00:00
|
|
|
not inB and eS.push(xA)
|
2002-04-27 20:31:59 +00:00
|
|
|
elsif (xB < xA)
|
|
|
|
|
iB += 1
|
|
|
|
|
inB = ! inB
|
2002-08-01 09:22:29 +00:00
|
|
|
not inA and eS.push(xB)
|
2002-04-27 20:31:59 +00:00
|
|
|
else
|
|
|
|
|
iA += 1
|
|
|
|
|
iB += 1
|
|
|
|
|
inA = ! inA
|
|
|
|
|
inB = ! inB
|
2002-08-01 09:22:29 +00:00
|
|
|
inA == inB and eS.push(xA)
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2002-08-01 11:14:35 +00:00
|
|
|
iA < eA.length and (! inB) and eS.concat(eA[iA..eA.length])
|
|
|
|
|
iB < eB.length and (! inA) and eS.concat(eB[iB..eB.length])
|
2002-04-27 20:31:59 +00:00
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
s.set_pos_inf(pos_inf? || b.pos_inf?)
|
2002-04-27 20:31:59 +00:00
|
|
|
s.set_edges(eS)
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def intersect(set_spec)
|
|
|
|
|
b = _real_set(set_spec)
|
|
|
|
|
s = IntSpan.new
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
s.set_neg_inf(neg_inf? && b.neg_inf?)
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
eA = @set["edges"]
|
|
|
|
|
eB = b.edges
|
|
|
|
|
eS = s.edges
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
inA = neg_inf?
|
2005-03-09 14:22:52 +00:00
|
|
|
inB = b.neg_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
iA = 0
|
|
|
|
|
iB = 0
|
|
|
|
|
|
|
|
|
|
while (iA < eA.length and iB < eB.length)
|
|
|
|
|
xA = eA[iA]
|
|
|
|
|
xB = eB[iB]
|
|
|
|
|
|
|
|
|
|
if (xA < xB)
|
|
|
|
|
iA += 1
|
|
|
|
|
inA = ! inA
|
2002-08-01 09:22:29 +00:00
|
|
|
inB and eS.push(xA)
|
2002-04-27 20:31:59 +00:00
|
|
|
elsif (xB < xA)
|
|
|
|
|
iB += 1
|
|
|
|
|
inB = ! inB
|
2002-08-01 09:22:29 +00:00
|
|
|
inA and eS.push(xB)
|
2002-04-27 20:31:59 +00:00
|
|
|
else
|
|
|
|
|
iA += 1
|
|
|
|
|
iB += 1
|
|
|
|
|
inA = ! inA
|
|
|
|
|
inB = ! inB
|
2002-08-01 09:22:29 +00:00
|
|
|
inA == inB and eS.push(xA)
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2002-08-01 11:14:35 +00:00
|
|
|
iA < eA.length and inB and eS.concat(eA[iA..eA.length])
|
|
|
|
|
iB < eB.length and inA and eS.concat(eB[iB..eB.length])
|
2002-04-27 20:31:59 +00:00
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
s.set_neg_inf(pos_inf? && b.pos_inf?)
|
2002-04-27 20:31:59 +00:00
|
|
|
s.set_edges(eS)
|
|
|
|
|
return s
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def diff (set_spec)
|
|
|
|
|
b = _real_set(set_spec)
|
|
|
|
|
s = IntSpan.new
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
s.set_neg_inf(neg_inf? && ! b.neg_inf?)
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
eA = @set["edges"]
|
|
|
|
|
eB = b.edges
|
|
|
|
|
eS = s.edges
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
inA = neg_inf?
|
2005-03-09 14:22:52 +00:00
|
|
|
inB = b.neg_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
iA = 0
|
|
|
|
|
iB = 0
|
|
|
|
|
|
|
|
|
|
while (iA < eA.length and iB < eB.length)
|
|
|
|
|
xA = eA[iA]
|
|
|
|
|
xB = eB[iB]
|
|
|
|
|
|
|
|
|
|
if (xA < xB)
|
|
|
|
|
iA += 1
|
|
|
|
|
inA = ! inA
|
2002-08-01 09:22:29 +00:00
|
|
|
not inB and eS.push(xA)
|
2002-04-27 20:31:59 +00:00
|
|
|
elsif (xB < xA)
|
|
|
|
|
iB += 1
|
|
|
|
|
inB = ! inB
|
2002-08-01 09:22:29 +00:00
|
|
|
inA and eS.push(xB)
|
2002-04-27 20:31:59 +00:00
|
|
|
else
|
|
|
|
|
iA += 1
|
|
|
|
|
iB += 1
|
|
|
|
|
inA = ! inA
|
|
|
|
|
inB = ! inB
|
2002-08-01 09:22:29 +00:00
|
|
|
inA != inB and eS.push(xA)
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2002-08-01 11:14:35 +00:00
|
|
|
iA < eA.length and not inB and eS.concat(eA[iA..eA.length])
|
|
|
|
|
iB < eB.length and inA and eS.concat(eB[iB..eB.length])
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
s.set_edges(eS)
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
s.set_pos_inf(pos_inf? && ! b.pos_inf?)
|
2002-04-27 20:31:59 +00:00
|
|
|
return s
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def xor(set_spec)
|
|
|
|
|
b = _real_set(set_spec)
|
|
|
|
|
s = IntSpan.new
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
s.set_neg_inf(neg_inf? ^ b.neg_inf?)
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
eA = @set["edges"]
|
|
|
|
|
eB = b.edges
|
|
|
|
|
eS = s.edges
|
|
|
|
|
|
|
|
|
|
iA = 0
|
|
|
|
|
iB = 0
|
|
|
|
|
|
|
|
|
|
while (iA < eA.length and iB < eB.length)
|
|
|
|
|
xA = eA[iA]
|
|
|
|
|
xB = eB[iB]
|
|
|
|
|
|
|
|
|
|
if (xA < xB)
|
|
|
|
|
iA += 1
|
2002-08-01 09:22:29 +00:00
|
|
|
eS.push(xA)
|
2002-04-27 20:31:59 +00:00
|
|
|
elsif (xB < xA)
|
|
|
|
|
iB += 1
|
2002-08-01 09:22:29 +00:00
|
|
|
eS.push(xB)
|
2002-04-27 20:31:59 +00:00
|
|
|
else
|
|
|
|
|
iA += 1
|
|
|
|
|
iB += 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2002-08-01 11:14:35 +00:00
|
|
|
iA < eA.length and eS.concat(eA[iA..eA.length])
|
|
|
|
|
iB < eB.length and eS.concat(eB[iB..eB.length])
|
2002-04-27 20:31:59 +00:00
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
s.set_pos_inf(pos_inf? ^ b.pos_inf?)
|
2002-04-27 20:31:59 +00:00
|
|
|
s.set_edges(eS)
|
|
|
|
|
return s
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def complement
|
|
|
|
|
# complement is inverse set; dit klopt hier dus niet
|
|
|
|
|
a = first
|
|
|
|
|
b = last
|
|
|
|
|
|
|
|
|
|
print "first #{a} last #{b}\n" if Debuglevel > 0
|
|
|
|
|
if a!=b
|
|
|
|
|
s = IntSpan.new("#{a}-#{b}")
|
|
|
|
|
comp = xor(s)
|
|
|
|
|
else
|
|
|
|
|
comp = IntSpan.new("#{a}")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if Debuglevel > 0
|
|
|
|
|
while i = comp.next
|
|
|
|
|
print "#{i}\n"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2005-03-09 14:22:52 +00:00
|
|
|
comp.set_neg_inf(! comp.neg_inf?)
|
|
|
|
|
comp.set_pos_inf(! comp.pos_inf?)
|
2002-04-27 20:31:59 +00:00
|
|
|
return comp
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def superset(set_spec)
|
|
|
|
|
b = _real_set(set_spec)
|
|
|
|
|
|
|
|
|
|
s = b.diff(self)
|
2005-03-09 14:22:52 +00:00
|
|
|
return s.empty?
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def subset(set_spec)
|
|
|
|
|
b = _real_set(set_spec)
|
|
|
|
|
|
|
|
|
|
s = diff(b)
|
2005-03-09 14:22:52 +00:00
|
|
|
return s.empty?
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def equal(set_spec)
|
|
|
|
|
b = _real_set(set_spec)
|
|
|
|
|
|
|
|
|
|
print "a\n"
|
2005-03-09 15:02:21 +00:00
|
|
|
neg_inf? == b.neg_inf? or return false
|
2002-04-27 20:31:59 +00:00
|
|
|
print "b\n"
|
2005-03-09 15:02:21 +00:00
|
|
|
pos_inf? == b.pos_inf? or return false
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
aEdge = @set["edges"]
|
|
|
|
|
bEdge = b.edges
|
|
|
|
|
print "aEdge #{aEdge.length} bEdge #{bEdge.length}\n"
|
|
|
|
|
aEdge.length == bEdge.length or return false
|
|
|
|
|
print "c\n"
|
|
|
|
|
|
|
|
|
|
for i in (0...aEdge.length)
|
|
|
|
|
aEdge[i] == bEdge[i] or return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def equivalent(set_spec)
|
|
|
|
|
b = _real_set(set_spec)
|
|
|
|
|
|
|
|
|
|
cardinality == b.cardinality
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def cardinality
|
2005-03-09 15:02:21 +00:00
|
|
|
(neg_inf? or pos_inf?) and return -1
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
car = 0
|
|
|
|
|
edges = @set["edges"]
|
|
|
|
|
i=0
|
|
|
|
|
while (i < edges.length)
|
|
|
|
|
lower = edges[i]
|
|
|
|
|
upper = edges[i+1]
|
|
|
|
|
car += upper - lower
|
|
|
|
|
i += 2
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return car
|
|
|
|
|
end
|
|
|
|
|
|
2005-03-09 14:22:52 +00:00
|
|
|
def empty?
|
|
|
|
|
if neg_inf? || pos_inf? || @set["edges"].length > 0
|
2002-04-27 20:31:59 +00:00
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def edges
|
|
|
|
|
return @set["edges"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def set_edges(edges)
|
|
|
|
|
@set["edges"] = edges
|
|
|
|
|
end
|
|
|
|
|
|
2005-03-09 14:22:52 +00:00
|
|
|
def neg_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
return @set["negInf"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def set_neg_inf(negInf)
|
|
|
|
|
@set["negInf"] = negInf
|
|
|
|
|
end
|
|
|
|
|
|
2005-03-09 14:22:52 +00:00
|
|
|
def pos_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
return @set["posInf"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def set_pos_inf(posInf)
|
|
|
|
|
@set["posInf"] = posInf
|
|
|
|
|
end
|
|
|
|
|
|
2005-03-09 18:14:56 +00:00
|
|
|
def finite?
|
|
|
|
|
return (! neg_inf? and ! pos_inf?)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def infinite?
|
|
|
|
|
return ! finite?
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def universal
|
2005-03-09 15:02:21 +00:00
|
|
|
neg_inf? and not @set["edges"].length > 0 and pos_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
|
2005-05-09 11:52:26 +00:00
|
|
|
# XXX this should probably have some recursion to make it faster
|
|
|
|
|
def member?(n)
|
2005-03-09 15:02:21 +00:00
|
|
|
inSet = neg_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
edge = @set["edges"]
|
|
|
|
|
|
|
|
|
|
for i in (0...edge.length)
|
|
|
|
|
if inSet
|
|
|
|
|
return true if n <= edge[i]
|
|
|
|
|
inSet = false
|
|
|
|
|
else
|
|
|
|
|
return false if n <= edge[i]
|
|
|
|
|
inSet = true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
inSet
|
|
|
|
|
end
|
|
|
|
|
|
2008-02-12 15:16:53 +00:00
|
|
|
def insert!(n)
|
2008-07-16 17:47:32 +00:00
|
|
|
if @set["edges"].empty?
|
2002-04-27 22:10:43 +00:00
|
|
|
@set["edges"] = [n-1, n]
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
2008-07-16 17:47:32 +00:00
|
|
|
if n == @set["edges"][-1]+1
|
|
|
|
|
@set["edges"][-1] += 1
|
2002-04-27 20:31:59 +00:00
|
|
|
return
|
2008-02-12 15:16:53 +00:00
|
|
|
elsif n > @set["edges"][-1]
|
2008-07-16 17:47:32 +00:00
|
|
|
@set["edges"].push(n-1, n)
|
2002-04-27 20:31:59 +00:00
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
2005-03-09 18:14:56 +00:00
|
|
|
# XXX dit kan vast netter... toch de Dijkstra neuronen nog eens aansteken
|
2005-05-09 11:52:26 +00:00
|
|
|
# XXX this should probably have some recursion to make it faster
|
2005-03-09 18:14:56 +00:00
|
|
|
l = 0
|
2008-02-12 15:16:53 +00:00
|
|
|
r = @set["edges"].length-1
|
2005-03-09 18:14:56 +00:00
|
|
|
i = r/2
|
|
|
|
|
while true
|
2008-02-12 15:16:53 +00:00
|
|
|
if n < @set["edges"][i] && n > @set["edges"][i-1]
|
2005-03-09 18:14:56 +00:00
|
|
|
inSet = i.odd?
|
|
|
|
|
break
|
2008-02-12 15:16:53 +00:00
|
|
|
elsif n < @set["edges"][i] && i == 0
|
2002-04-27 20:31:59 +00:00
|
|
|
inSet = false
|
2005-03-09 18:14:56 +00:00
|
|
|
break
|
2008-02-12 15:16:53 +00:00
|
|
|
elsif n < @set["edges"][i]
|
2005-03-09 18:14:56 +00:00
|
|
|
r = i
|
|
|
|
|
i = l + ((r-l)/2)
|
2008-07-16 17:47:32 +00:00
|
|
|
# tikkeltje sneller dan neg_inf? aanroepen
|
|
|
|
|
inSet = @set["negInf"]
|
2008-02-12 15:16:53 +00:00
|
|
|
elsif n == @set["edges"][i]
|
2005-03-09 18:14:56 +00:00
|
|
|
inSet = i.odd?
|
|
|
|
|
break
|
2002-04-27 20:31:59 +00:00
|
|
|
else
|
2005-03-09 18:14:56 +00:00
|
|
|
l = i
|
|
|
|
|
i = l + ((r-l)/2)
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
inSet and return
|
|
|
|
|
|
2008-02-12 15:16:53 +00:00
|
|
|
lGap = i == 0 || n-1 - @set["edges"][i-1]
|
2002-04-27 20:31:59 +00:00
|
|
|
lGap = false if lGap == 0
|
|
|
|
|
|
2008-02-12 15:16:53 +00:00
|
|
|
rGap = i == @set["edges"].length-1 ? i : @set["edges"][i] - n
|
2002-04-27 20:31:59 +00:00
|
|
|
rGap = false if rGap == 0
|
|
|
|
|
|
|
|
|
|
if ( lGap and rGap)
|
2008-07-16 17:47:32 +00:00
|
|
|
@set["edges"].insert(i, n-1, n)
|
2002-04-27 20:31:59 +00:00
|
|
|
elsif (not lGap and rGap)
|
2008-02-12 15:16:53 +00:00
|
|
|
@set["edges"][i-1] += 1
|
2002-04-27 20:31:59 +00:00
|
|
|
elsif ( lGap and not rGap)
|
2008-02-12 15:16:53 +00:00
|
|
|
@set["edges"][i] -= 1
|
2002-04-27 20:31:59 +00:00
|
|
|
else
|
2008-07-16 17:47:32 +00:00
|
|
|
@set["edges"].slice!(i-1, 2)
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2005-03-09 18:14:56 +00:00
|
|
|
def remove!(n)
|
2002-04-27 20:31:59 +00:00
|
|
|
n or return
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
inSet = neg_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
edge = @set["edges"]
|
|
|
|
|
|
|
|
|
|
for i in (0...edge.length)
|
|
|
|
|
if (inSet)
|
|
|
|
|
break if n <= edge[i]
|
|
|
|
|
inSet = false
|
|
|
|
|
else
|
|
|
|
|
return if n <= edge[i]
|
|
|
|
|
inSet = true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return unless inSet
|
|
|
|
|
|
|
|
|
|
for i in (0...edge.length)
|
|
|
|
|
if edge[i] == n-1 and edge[i+1] == n
|
|
|
|
|
lower = edge[0...i]
|
|
|
|
|
upper = edge[i+2..edge.length]
|
|
|
|
|
edge = lower + upper
|
|
|
|
|
break
|
|
|
|
|
elsif edge[i] == n-1
|
|
|
|
|
edge[i] += 1
|
|
|
|
|
break
|
|
|
|
|
elsif edge[i] == n
|
|
|
|
|
edge[i] += 1
|
|
|
|
|
break
|
|
|
|
|
elsif edge[i+1] == n
|
|
|
|
|
edge[i+1] -= 1
|
|
|
|
|
break
|
|
|
|
|
elsif edge[i]<n and edge[i+1]>n
|
|
|
|
|
lower = edge[0..i]
|
|
|
|
|
upper = edge[i+1..edge.length]
|
|
|
|
|
edge = lower + [n-1, n] +upper
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
i += 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@set["edges"] = edge
|
2005-03-09 18:14:56 +00:00
|
|
|
return self
|
2002-04-27 20:31:59 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def min
|
2005-03-09 14:22:52 +00:00
|
|
|
empty? and return nil
|
|
|
|
|
neg_inf? and return nil
|
2002-04-27 20:31:59 +00:00
|
|
|
@set["edges"][0]+1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def max
|
2005-03-09 14:22:52 +00:00
|
|
|
empty? and return nil
|
|
|
|
|
pos_inf? and return nil
|
2002-04-27 20:31:59 +00:00
|
|
|
@set["edges"][-1]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def grep_set(block)
|
2005-03-09 15:02:21 +00:00
|
|
|
return nil if neg_inf? or pos_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
edges = @set["edges"]
|
|
|
|
|
sub_edges = []
|
|
|
|
|
|
|
|
|
|
while (edges.length > 0)
|
2008-07-16 17:47:32 +00:00
|
|
|
lower, upper = edges.slice!(0..1)
|
|
|
|
|
#lower = edges[0]
|
|
|
|
|
#upper = edges[1]
|
|
|
|
|
#edges = edges.slice(2..edges.length)
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
for i in (lower+1..upper)
|
|
|
|
|
# local $_ = i
|
|
|
|
|
# &$block() or next # definately wrong, must eval block
|
|
|
|
|
|
|
|
|
|
if (sub_edges.length > 0 and sub_edges[-1] == i-1)
|
|
|
|
|
sub_edges[-1] = i
|
|
|
|
|
else
|
|
|
|
|
sub_edges += [ i-1, i ]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
sub_set = new
|
|
|
|
|
sub_set["edges"] = sub_edges
|
|
|
|
|
sub_set
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def map_set(block)
|
2005-03-09 15:02:21 +00:00
|
|
|
return nil if neg_inf? or pos_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
map_set = new
|
|
|
|
|
|
|
|
|
|
edges = @set["edges"]
|
|
|
|
|
while (edges.length > 0)
|
2008-07-16 17:47:32 +00:00
|
|
|
lower, upper = edges.slice!(0..1)
|
|
|
|
|
#lower = edges[0]
|
|
|
|
|
#upper = edges[1]
|
|
|
|
|
#edges = edges.slice(2..edges.length)
|
2002-04-27 20:31:59 +00:00
|
|
|
|
|
|
|
|
for domain in (lower+1..upper)
|
|
|
|
|
local $_ = domain;
|
|
|
|
|
|
|
|
|
|
# for range (&$block()) # definately wrong, must eval block
|
|
|
|
|
# map_set.insert(range)
|
|
|
|
|
# end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
map_set
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def first
|
|
|
|
|
@set["iterator"] = min
|
|
|
|
|
@set["run"] = []
|
|
|
|
|
@set["run"][0] = 0
|
|
|
|
|
@set["run"][1] = @set["edges"].length > 0 ? 1 : nil
|
|
|
|
|
|
|
|
|
|
@set["iterator"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def last
|
|
|
|
|
lastEdge = @set["edges"].length - 1
|
|
|
|
|
@set["iterator"] = max
|
|
|
|
|
@set["run"][0] = lastEdge > 0 ? lastEdge-1 : nil
|
|
|
|
|
@set["run"][1] = lastEdge
|
|
|
|
|
|
|
|
|
|
@set["iterator"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def start(startval)
|
|
|
|
|
set["iterator"] = nil
|
|
|
|
|
startval or return nil
|
|
|
|
|
|
2005-03-09 15:02:21 +00:00
|
|
|
inSet = neg_inf?
|
2002-04-27 20:31:59 +00:00
|
|
|
edges = @set["edges"]
|
|
|
|
|
|
|
|
|
|
for i in (0...edges.length)
|
|
|
|
|
if (inSet)
|
|
|
|
|
if (startval <= edges[i])
|
|
|
|
|
@set["iterator"] = startval
|
|
|
|
|
@set["run"][0] = i ? i-1 : nil
|
|
|
|
|
@set["run"][1] = i
|
|
|
|
|
return $startval
|
|
|
|
|
end
|
|
|
|
|
inSet = false
|
|
|
|
|
else
|
|
|
|
|
if (startval <= edges[i])
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
inSet = true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if (inSet)
|
|
|
|
|
@set["iterator"] = startval
|
|
|
|
|
@set["run"][0] = edges.length > 0 ? edges.length: nil
|
|
|
|
|
@set["run"][1] = nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@set["iterator"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def current
|
|
|
|
|
@set["iterator"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def next
|
|
|
|
|
@set["iterator"] or return first
|
|
|
|
|
|
|
|
|
|
run1 = @set["run"][1]
|
|
|
|
|
run1 or return ++@set["iterator"]
|
|
|
|
|
|
|
|
|
|
edges = @set["edges"]
|
|
|
|
|
if (@set["iterator"] < edges[run1])
|
|
|
|
|
@set["iterator"] += 1
|
|
|
|
|
return @set["iterator"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if (run1 < edges.length-2)
|
|
|
|
|
run0 = run1 + 1
|
|
|
|
|
@set["run"] = [run0, run0+1]
|
|
|
|
|
@set["iterator"] = edges[run0]+1
|
|
|
|
|
elsif (run1 < edges.length-1)
|
|
|
|
|
run0 = run1 + 1
|
|
|
|
|
@set["run"] = [run0, nil]
|
|
|
|
|
@set["iterator"] = edges[run0]+1
|
|
|
|
|
else
|
|
|
|
|
@set["iterator"] = nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@set["iterator"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def prev
|
|
|
|
|
@set["iterator"] or return last
|
|
|
|
|
|
|
|
|
|
run0 = @set["run"][0]
|
|
|
|
|
run0 or return --@set["iterator"]
|
|
|
|
|
|
|
|
|
|
edges = @set["edges"]
|
|
|
|
|
|
|
|
|
|
if (@set["iterator"] > edges[run0]+1)
|
|
|
|
|
@set["iterator"] -= 1
|
|
|
|
|
return @set["iterator"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if (run0 > 1)
|
|
|
|
|
run1 = run0 - 1
|
|
|
|
|
@set["run"] = [run1-1, run1]
|
|
|
|
|
@set["iterator"] = edges[run1]
|
|
|
|
|
elsif (run0 > 0)
|
|
|
|
|
run1 = run0 - 1
|
|
|
|
|
@set["run"] = [nil, run1]
|
|
|
|
|
@set["iterator"] = edges[run1]
|
|
|
|
|
else
|
|
|
|
|
@set["iterator"] = nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@set["iterator"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end # class
|
|
|
|
|
|
|
|
|
|
end # module
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO
|
|
|
|
|
# Do not kill an item until it's tested!
|
|
|
|
|
|
|
|
|
|
# [x] new
|
|
|
|
|
# [x] valid
|
|
|
|
|
# [ ] copy
|
|
|
|
|
# [ ] _copy_empty # makes $set the empty set
|
2002-04-27 23:41:29 +00:00
|
|
|
# [x] _copy_array # copies an array into a set
|
2002-04-27 20:31:59 +00:00
|
|
|
# [ ] _copy_set # copies one set to another
|
|
|
|
|
# [ ] _copy_run_list # parses a run list
|
|
|
|
|
# [x] run_list
|
|
|
|
|
# [x] elements
|
|
|
|
|
# [x] _real_set # converts a set specification into a set
|
|
|
|
|
# [x] union
|
|
|
|
|
# [x] intersect
|
|
|
|
|
# [x] diff
|
|
|
|
|
# [x] xor
|
|
|
|
|
# [ ] complement
|
|
|
|
|
# [x] superset
|
|
|
|
|
# [x] subset
|
|
|
|
|
# [x] equal
|
|
|
|
|
# [x] equivalent
|
|
|
|
|
# [x] cardinality
|
2005-03-09 14:22:52 +00:00
|
|
|
# [x] empty?
|
2002-04-27 20:31:59 +00:00
|
|
|
# [x] finite
|
2005-03-09 14:22:52 +00:00
|
|
|
# [x] neg_inf? { shift->{negInf} }
|
|
|
|
|
# [x] pos_inf? { shift->{posInf} }
|
2002-04-27 20:31:59 +00:00
|
|
|
# [x] infinite
|
|
|
|
|
# [ ] universal
|
|
|
|
|
# [x] member
|
2002-04-27 22:10:43 +00:00
|
|
|
# [x] insert # way to much code i think
|
2002-04-27 20:31:59 +00:00
|
|
|
# [x] remove
|
|
|
|
|
# [x] min
|
|
|
|
|
# [x] max
|
|
|
|
|
# [ ] grep_set(&$)
|
|
|
|
|
# [ ] map_set(&$)
|
|
|
|
|
# [x] first($)
|
|
|
|
|
# [x] last($)
|
|
|
|
|
# [ ] start($$)
|
|
|
|
|
# [x] current($) { shift->{iterator} }
|
|
|
|
|
# [x] next($)
|
|
|
|
|
# [x] prev($)
|
|
|
|
|
|
|
|
|
|
# New methods
|
|
|
|
|
# [x] set_neg_inf
|
|
|
|
|
# [x] set_pos_inf
|
|
|
|
|
# [x] set_edges
|
|
|
|
|
# [x] edges
|