File: multiplication.rb

package info (click to toggle)
ruby-rgfa 1.3.1%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 820 kB
  • sloc: ruby: 5,649; makefile: 9
file content (156 lines) | stat: -rw-r--r-- 5,003 bytes parent folder | download | duplicates (4)
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