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
use std::cmp;

use noodles_core::Position;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Context {
    reference_sequence_id: usize,
    alignment_start: Position,
    alignment_end: Position,
}

impl Context {
    fn new(
        reference_sequence_id: usize,
        alignment_start: Position,
        alignment_end: Position,
    ) -> Self {
        Self {
            reference_sequence_id,
            alignment_start,
            alignment_end,
        }
    }

    pub fn reference_sequence_id(&self) -> usize {
        self.reference_sequence_id
    }

    pub fn alignment_start(&self) -> Position {
        self.alignment_start
    }

    pub fn alignment_span(&self) -> usize {
        usize::from(self.alignment_end) - usize::from(self.alignment_start) + 1
    }

    pub fn alignment_end(&self) -> Position {
        self.alignment_end
    }
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ReferenceSequenceContext {
    Some(Context),
    #[default]
    None,
    Many,
}

impl ReferenceSequenceContext {
    pub fn some(
        reference_sequence_id: usize,
        alignment_start: Position,
        alignment_end: Position,
    ) -> Self {
        Self::Some(Context::new(
            reference_sequence_id,
            alignment_start,
            alignment_end,
        ))
    }

    pub fn is_many(&self) -> bool {
        matches!(self, Self::Many)
    }

    pub fn update(
        &mut self,
        reference_sequence_id: Option<usize>,
        alignment_start: Option<Position>,
        alignment_end: Option<Position>,
    ) {
        *self = match (*self, reference_sequence_id, alignment_start, alignment_end) {
            (Self::Some(context), Some(record_id), Some(record_start), Some(record_end)) => {
                if record_id == context.reference_sequence_id() {
                    let start = cmp::min(record_start, context.alignment_start());
                    let end = cmp::max(record_end, context.alignment_end());
                    Self::some(record_id, start, end)
                } else {
                    Self::Many
                }
            }
            (Self::Some(..), ..) => Self::Many,
            (Self::None, Some(_), ..) => Self::Many,
            (Self::None, None, ..) => Self::None,
            (Self::Many, ..) => Self::Many,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_update() -> Result<(), noodles_core::position::TryFromIntError> {
        let mut context =
            ReferenceSequenceContext::some(0, Position::try_from(8)?, Position::try_from(13)?);
        context.update(Some(0), Position::new(5), Position::new(21));
        assert_eq!(
            context,
            ReferenceSequenceContext::some(0, Position::try_from(5)?, Position::try_from(21)?)
        );

        let mut context =
            ReferenceSequenceContext::some(0, Position::try_from(8)?, Position::try_from(13)?);
        context.update(None, None, None);
        assert_eq!(context, ReferenceSequenceContext::Many);

        let mut context = ReferenceSequenceContext::None;
        context.update(Some(0), Some(Position::MIN), Some(Position::MIN));
        assert_eq!(context, ReferenceSequenceContext::Many);

        let mut context = ReferenceSequenceContext::None;
        context.update(None, None, None);
        assert_eq!(context, ReferenceSequenceContext::None);

        let mut context = ReferenceSequenceContext::Many;
        context.update(Some(0), Some(Position::MIN), Some(Position::MIN));
        assert_eq!(context, ReferenceSequenceContext::Many);

        let mut context = ReferenceSequenceContext::Many;
        context.update(None, None, None);
        assert_eq!(context, ReferenceSequenceContext::Many);

        Ok(())
    }
}