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
use crate::{Monotonicity, Regulation, RegulatoryGraph, VariableId, ID_REGEX_STR};
use regex::Regex;

/// **(internal)** Regex which matches the regulation arrow string with `monotonicity`
/// and `observable` groups.
const REGULATION_ARROW_REGEX_STR: &str = r"-(?P<monotonicity>[|>?])(?P<observable>\??)";

lazy_static! {
    /// **(internal)** A regex which reads one line specifying a regulation.
    static ref REGULATION_REGEX: Regex = Regex::new(
        format!(
            // regulator ID, whitespace?, arrow string, whitespace?, target ID
            r"^(?P<regulator>{})\s*{}\s*(?P<target>{})$",
            ID_REGEX_STR,
            REGULATION_ARROW_REGEX_STR,
            ID_REGEX_STR,
        ).as_str()
    ).unwrap();
}

/// Basic getters.
impl Regulation {
    /// Check if the regulation is marked as observable.
    pub fn is_observable(&self) -> bool {
        self.observable
    }

    /// Return monotonicity of the regulation (if specified).
    pub fn get_monotonicity(&self) -> Option<Monotonicity> {
        self.monotonicity
    }

    /// Get the `VariableId` if the regulator.
    pub fn get_regulator(&self) -> VariableId {
        self.regulator
    }

    /// Get the `VariableId` of the target.
    pub fn get_target(&self) -> VariableId {
        self.target
    }
}

/// Serialization utility methods.
impl Regulation {
    /// Try to read all available information about a regulation from a given string
    /// in the standard format.
    ///
    /// The returned data correspond to the items as they appear in the string, i.e. `regulator`,
    /// `monotonicity`, `observability` and `target`. If the string is not valid, returns `None`.
    pub fn try_from_string(
        regulation: &str,
    ) -> Option<(String, Option<Monotonicity>, bool, String)> {
        REGULATION_REGEX
            .captures(regulation.trim())
            .map(|captures| {
                let monotonicity = match &captures["monotonicity"] {
                    "?" => None,
                    "|" => Some(Monotonicity::Inhibition),
                    ">" => Some(Monotonicity::Activation),
                    _ => unreachable!("Nothing else matches this group."),
                };
                let observable = captures["observable"].is_empty();
                (
                    captures["regulator"].to_string(),
                    monotonicity,
                    observable,
                    captures["target"].to_string(),
                )
            })
    }

    /// Convert to standard string format using variable names provided by a `RegulatoryGraph`.
    pub fn to_string(&self, context: &RegulatoryGraph) -> String {
        let monotonicity = match self.get_monotonicity() {
            None => "?",
            Some(Monotonicity::Activation) => ">",
            Some(Monotonicity::Inhibition) => "|",
        };
        let observability = if self.is_observable() { "" } else { "?" };
        format!(
            "{} -{}{} {}",
            context.get_variable_name(self.regulator),
            monotonicity,
            observability,
            context.get_variable_name(self.target)
        )
    }
}

#[cfg(test)]
mod tests {
    use crate::{BooleanNetwork, Regulation};
    use std::convert::TryFrom;

    #[test]
    fn regulation_conversion() {
        let bn = BooleanNetwork::try_from(
            r"
            a -?? b
            b -? c
            c ->? d
            d -> e
            e -|? f
            f -| g
        ",
        )
        .unwrap();

        for regulation in bn.graph.regulations() {
            let (r, m, o, t) =
                Regulation::try_from_string(&regulation.to_string(bn.as_graph())).unwrap();
            assert_eq!(&r, bn.get_variable_name(regulation.get_regulator()));
            assert_eq!(&t, bn.get_variable_name(regulation.get_target()));
            assert_eq!(m, regulation.get_monotonicity());
            assert_eq!(o, regulation.is_observable());
        }

        assert_eq!(None, Regulation::try_from_string("a --> b"));
    }
}