File: multiplication.rb

package info (click to toggle)
ruby-rgfa 1.3.1%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 848 kB
  • sloc: ruby: 5,666; makefile: 9
file content (194 lines) | stat: -rw-r--r-- 7,536 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
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