1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
#
# Methods which edit the graph components without traversal
#
module RGFATools::Multiplication
# Allowed values for the links_distribution_policy option
LINKS_DISTRIBUTION_POLICY = [:off, :auto, :equal, :E, :B]
# @overload multiply(segment, factor, copy_names: :lowcase, distribute: :auto, conserve_components: true, origin_tag: :or)
# Create multiple copies of a segment.
#
# Complements the multiply method of gfatools with additional functionality.
# These extensions are used only after #enable_extensions is called on the
# RGFA object. After that, you may still call the original method
# using #multiply_without_rgfatools.
#
# For more information on the additional functionality, see
# #multiply_extended.
#
# @return [RGFA] self
def multiply_with_rgfatools(segment, factor,
copy_names: :lowcase,
distribute: :auto,
conserve_components: true,
origin_tag: :or)
if !@extensions_enabled
return multiply_without_rgfatools(segment, factor,
copy_names: copy_names,
conserve_components: conserve_components)
else
multiply_extended(segment, factor,
copy_names: copy_names,
distribute: distribute,
conserve_components: conserve_components,
origin_tag: origin_tag)
end
end
# Create multiple copies of a segment.
#
# Complements the multiply method of gfatools with additional functionality.
# To always run the additional functionality when multiply is called,
# use RGFA#enable_extensions.
#
# @!macro [new] copynames_text
#
# <b>Automatic computation of the copy names:</b>
#
# - First, itis checked if the name of the original segment ends with a
# relevant
# string, i.e. a lower case letter (for +:lowcase+), an upper case letter
# (for +:upcase+), a digit (for +:number+), or the string +"_copy"+
# plus one or more optional digits (for +:copy+).
# - If so, it is assumed, it was already a copy, and it is not
# altered.
# - If not, then +a+ (for +:lowcase+), +A+ (for +:upcase+), +1+ (for
# +:number+), +_copy+ (for +:copy+) is appended to the string.
# - Then, in all
# cases, next (*) is called on the string, until a valid, non-existant
# name is found for each of the segment copies
# - (*) = except for +:copy+, where
# for the first copy no digit is present, but for the following is,
# i.e. the segment names will be +:copy+, +:copy2+, +:copy3+, etc.
# - Can be overridden, by providing an array of copy names.
#
# @!macro [new] ldp_text
#
# <b>Links distribution policy</b>
#
# Depending on the value of the option +distribute+, an end
# is eventually selected for distribution of the links.
#
# - +:off+: no distribution performed
# - +:E+: links of the E end are distributed
# - +:B+: links of the B end are distributed
# - +:equal+: select an end for which the number of links is equal to
# +factor+, if any; if both, then the E end is selected
# - +:auto+: automatically select E or B, trying to maximize the number of
# links which can be deleted
#
# @param [Integer] factor multiplication factor; if 0, delete the segment;
# if 1; do nothing; if > 1; number of copies to create
# @!macro [new] segment_param
# @param segment [String, RGFA::Line::Segment] segment name or instance
# @param [:lowcase, :upcase, :number, :copy, Array<String>] copy_names
# <i>(Defaults to: +:lowcase+)</i>
# Array of names for the copies of the segment,
# or a symbol, which defines a system to compute the names from the name of
# the original segment. See "Automatic computation of the copy names".
# @!macro [new] conserve_components
# @param [Boolean] conserve_components <i>(Defaults to: +true+)</i>
# If factor == 0 (i.e. deletion), delete segment only if
# #cut_segment?(segment) is +false+ (see RGFA API).
# @!macro [new] ldp_param
# @param distribute
# [RGFATools::Multiplication::LINKS_DISTRIBUTION_POLICY]
# <i>(Defaults to: +:auto+)</i>
# Determines if and for which end of the segment, links are distributed
# among the copies. See "Links distribution policy".
# @!macro [new] origin_tag
# @param origin_tag [Symbol] <i>(Defaults to: +:or+)</i>
# Name of the custom tag to use for storing origin information.
#
# @return [RGFA] self
def multiply_extended(segment, factor,
copy_names: :lowcase,
distribute: :auto,
conserve_components: true,
origin_tag: :or)
s, sn = segment_and_segment_name(segment)
s.set(origin_tag, sn) if !s.get(origin_tag)
copy_names = compute_copy_names(copy_names, sn, factor)
multiply_without_rgfatools(sn, factor,
copy_names: copy_names,
conserve_components: conserve_components)
distribute_links(distribute, sn, copy_names, factor)
return self
end
private
Redefined = [:multiply]
def select_distribute_end(links_distribution_policy, segment_name, factor)
accepted = RGFATools::Multiplication::LINKS_DISTRIBUTION_POLICY
if !accepted.include?(links_distribution_policy)
raise "Unknown links distribution policy #{links_distribution_policy}, "+
"accepted values are: "+
accepted.inspect
end
return nil if links_distribution_policy == :off
if [:B, :E].include?(links_distribution_policy)
return links_distribution_policy
end
esize = links_of([segment_name, :E]).size
bsize = links_of([segment_name, :B]).size
auto_select_distribute_end(factor, bsize, esize,
links_distribution_policy == :equal)
end
# (keep separate for testing)
def auto_select_distribute_end(factor, bsize, esize, equal_only)
if esize == factor
return :E
elsif bsize == factor
return :B
elsif equal_only
return nil
elsif esize < 2
return (bsize < 2) ? nil : :B
elsif bsize < 2
return :E
elsif esize < factor
return ((bsize <= esize) ? :E :
((bsize < factor) ? :B : :E))
elsif bsize < factor
return :B
else
return ((bsize <= esize) ? :B : :E)
end
end
def distribute_links(links_distribution_policy, segment_name,
copy_names, factor)
return if factor < 2
end_type = select_distribute_end(links_distribution_policy,
segment_name, factor)
return nil if end_type.nil?
et_links = links_of([segment_name, end_type])
diff = [et_links.size - factor, 0].max
links_signatures = et_links.map do |l|
l.other_end([segment_name, end_type]).join
end
([segment_name]+copy_names).each_with_index do |sn, i|
links_of([sn, end_type]).each do |l|
l_sig = l.other_end([sn, end_type]).join
to_save = links_signatures[i..i+diff].to_a
delete_link(l) unless to_save.include?(l_sig)
end
end
end
def segment_and_segment_name(segment_or_segment_name)
if segment_or_segment_name.kind_of?(RGFA::Line)
s = segment_or_segment_name
sn = segment_or_segment_name.name
else
sn = segment_or_segment_name.to_sym
s = segment(sn)
end
return s, sn
end
end
|