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
|
require_relative "error.rb"
#
# Method for the RGFA class, which allow to split a segment into
# multiple copies.
#
module RGFA::Multiplication
# Create multiple copies of a segment.
#
# == Automatic computation of the copy names
#
# - Can be overridden, by providing an array of copy names.
# - First, it is 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.
#
# @param [Integer] factor multiplication factor; if 0, delete the segment;
# if 1; do nothing; if > 1; number of copies to create
# @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".
# @param [Boolean] conserve_components <i>(Defaults to: +true+)</i>
# If factor == 0 (i.e. deletion), delete segment only if
# {#cut_segment?}(segment) is +false+.
#
# @return [RGFA] self
def multiply(segment, factor, copy_names: :lowcase,
conserve_components: true)
segment_name = segment.kind_of?(RGFA::Line) ? segment.name : segment
if factor < 2
return self if factor == 1
return self if cut_segment?(segment_name) and conserve_components
return delete_segment(segment_name)
end
s = segment!(segment_name)
divide_segment_and_connection_counts(s, factor)
copy_names = compute_copy_names(copy_names, segment_name, factor)
copy_names.each {|cn| clone_segment_and_connections(s, cn)}
return self
end
private
def compute_copy_names(copy_names, segment_name, factor)
return nil if factor < 2
accepted = [:lowcase, :upcase, :number, :copy]
if copy_names.kind_of?(Array)
return copy_names
elsif !accepted.include?(copy_names)
raise ArgumentError,
"copy_names shall be an array of names or one of: "+
accepted.inspect
end
retval = []
next_name = segment_name.to_s
case copy_names
when :lowcase
if next_name =~ /^.*[a-z]$/
next_name = next_name.next
else
next_name += "b"
end
when :upcase
if next_name =~ /^.*[A-Z]$/
next_name = next_name.next
else
next_name += "B"
end
when :number
if next_name =~ /^.*[0-9]$/
next_name = next_name.next
else
next_name += "2"
end
when :copy
if next_name =~ /^.*_copy(\d*)$/
next_name += "1" if $1 == ""
next_name = next_name.next
copy_names = :number
else
next_name += "_copy"
end
end
while retval.size < (factor-1)
while retval.include?(next_name) or
@segments.has_key?(next_name.to_sym) or
@paths.has_key?(next_name.to_sym)
if copy_names == :copy
next_name += "1"
copy_names = :number
end
next_name = next_name.next
end
retval << next_name
end
return retval
end
def divide_counts(gfa_line, factor)
[:KC, :RC, :FC].each do |count_tag|
if gfa_line.optional_fieldnames.include?(count_tag)
value = (gfa_line.get(count_tag).to_f / factor)
gfa_line.set(count_tag, value.to_i)
end
end
end
def divide_segment_and_connection_counts(segment, factor)
divide_counts(segment, factor)
[:links,:containments].each do |rt|
[:from,:to].each do |dir|
[:+, :-].each do |o|
segment.send(rt)[dir][o].each do |l|
# circular link counts shall be divided only ones
next if dir == :to and l.from == l.to
divide_counts(l, factor)
end
end
end
end
end
def clone_segment_and_connections(segment, clone_name)
cpy = segment.clone
cpy.name = clone_name
self << cpy
[:links,:containments].each do |rt|
[:from,:to].each do |dir|
[:+, :-].each do |o|
segment.send(rt)[dir][o].each do |l|
lc = l.clone
lc.set(dir, clone_name)
self << lc
end
end
end
end
end
end
|