File: path.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 (106 lines) | stat: -rw-r--r-- 3,211 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
# A path line of a RGFA file
class RGFA::Line::Path < RGFA::Line

  RECORD_TYPE = :P
  REQFIELDS = [:path_name, :segment_names, :overlaps]
  PREDEFINED_OPTFIELDS = []
  DATATYPE = {
    :path_name => :lbl,
    :segment_names => :lbs,
    :overlaps => :cgs,
  }

  define_field_methods!

  # @note The field names are derived from the RGFA specification at:
  #   https://github.com/pmelsted/RGFA-spec/blob/master/RGFA-spec.md#path-line
  #   and were made all downcase with _ separating words;
  #   the cigar and segment_name regexps and name were changed to better
  #   implement what written in the commentaries of the specification
  #   (i.e. name pluralized and regexp changed to a comma-separated list
  #   for segment_name of segment names and orientations and for cigar of
  #   CIGAR strings);

  # @return [Symbol] name of the path as symbol
  def to_sym
    name.to_sym
  end

  # Is the path circular? In this case the number of CIGARs must be
  # equal to the number of segments.
  # @return [Boolean]
  def circular?
    self.overlaps.size == self.segment_names.size
  end

  # Is the path linear? This is the case when the number of CIGARs
  # is equal to the number of segments minus 1, or the CIGARs are
  # represented by a single "*".
  def linear?
    !circular?
  end

  # Are the overlaps a single "*"? This is a compact representation of
  # a linear path where all CIGARs are "*"
  # @return [Boolean]
  def undef_overlaps?
    self.overlaps.size == 1 and self.overlaps[0].empty?
  end

  # The links to which the path refers; it can be an empty array
  # (e.g. from a line which is not embedded in a graph);
  # the boolean is true if the equivalent reverse link is used.
  # @return [Array<RGFA::Line::Link, Boolean>]
  def links
    @links ||= []
    @links
  end

  # computes the list of links which are required to support
  # the path
  # @return [Array<[RGFA::OrientedSegment, RGFA::OrientedSegment, RGFA::Cigar]>]
  #   an array, which elements are 3-tuples (from oriented segment,
  #   to oriented segment, cigar)
  def required_links
    has_undef_overlaps = self.undef_overlaps?
    retval = []
    self.segment_names.size.times do |i|
      j = i+1
      if j == self.segment_names.size
        circular? ? j = 0 : break
      end
      cigar = has_undef_overlaps ? [] : self.overlaps[i]
      retval << [self.segment_names[i], self.segment_names[j], cigar]
    end
    retval
  end

  private

  def validate_lists_size!
    n_overlaps = self.overlaps.size
    n_segments = self.segment_names.size
    if n_overlaps == n_segments - 1
      # case 1: linear path
      return true
    elsif n_overlaps == 1 and self.overlaps[0].empty?
      # case 2: linear path, single "*" to represent overlaps which are all "*"
      return true
    elsif n_overlaps == n_segments
      # case 3: circular path
    else
      raise RGFA::Line::Path::ListLengthsError,
        "Path has #{n_segments} oriented segments, "+
        "but #{n_overlaps} overlaps"
    end
  end

  def validate_record_type_specific_info!
    validate_lists_size!
  end


end

# Error raised if number of segments and overlaps are not consistent
class RGFA::Line::Path::ListLengthsError < RGFA::Error; end