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
use std::{ffi::CString, path::Path, pin::Pin, ptr};

use smile::{
    autocxx::{c_int, c_void, cxx::let_cxx_string, WithinBox},
    ffi::DSL_network,
};

use crate::{
    errors::{CIntExt, Result},
    traits::ToDslValue,
};

use super::{BayesianNetworkAlgorithm, InfluenceDiagramAlgorithm};

/// A Bayesian Network.
pub struct Network {
    pub(super) dsl_network: Pin<Box<DSL_network>>,
}

impl PartialEq for Network {
    fn eq(&self, other: &Self) -> bool {
        // TODO: compare nodes, arcs, beliefs, and all other network properties
        true
    }
}

impl Eq for Network {}

impl Network {
    /// Create a new uninitialized network.
    pub fn new_uninitialized() -> Self {
        let dsl_network = DSL_network::new().within_box();
        Self { dsl_network }
    }

    /// Create a new network from an XDSL string.
    ///
    /// Example usage:
    /// ```rust
    /// # use nice_smile::network::Network;
    /// # use nice_smile::errors::Result;
    /// # fn try_main() -> Result<()> {
    /// # let mut network1 = Network::new_uninitialized();
    /// # let xdsl = network1.to_xdsl_string()?;
    /// let network = Network::from_xdsl_string(&xdsl)?;
    /// # assert!(network == network1);
    /// assert_ne!(xdsl, "".to_string());
    /// # Ok(()) }
    /// ```
    pub fn from_xdsl_string(xdsl_string: impl AsRef<str>) -> Result<Self> {
        let mut network = Self::new_uninitialized();
        let null_void_ptr: *mut c_void = ptr::null_mut();

        unsafe {
            network
                .dsl_network
                .as_mut()
                .ReadString(CString::new(xdsl_string.as_ref())?.as_ptr(), null_void_ptr)
                .into_result()?
        }

        Ok(network)
    }

    /// Export the network to an XDSL string.
    ///
    /// Example usage:
    /// ```rust
    /// # use nice_smile::network::Network;
    /// # use nice_smile::errors::Result;
    /// # fn try_main() -> Result<()> {
    /// let mut network = Network::new_uninitialized();
    /// let xdsl = network.to_xdsl_string()?;
    /// assert_ne!(xdsl, "".to_string());
    /// # Ok(()) }
    /// ```
    pub fn to_xdsl_string(&mut self) -> Result<String> {
        let null_void_ptr: *mut c_void = ptr::null_mut();

        let_cxx_string!(xdsl = "");
        unsafe {
            self.dsl_network
                .as_mut()
                .WriteString(xdsl.as_mut(), null_void_ptr)
                .into_result()?;
        }

        Ok(xdsl.to_string())
    }

    /// Read a network from a file.
    ///
    /// The file type is determined automaticaly by the file_path extension.
    /// For the supported file types list, refer to the [smile::ffi::DSL_network::ReadFile].
    pub fn read_from_file(file_path: impl AsRef<Path>) -> Result<Network> {
        let mut network = Network::new_uninitialized();

        let null_void_ptr: *mut c_void = ptr::null_mut();
        let file_path =
            CString::new(file_path.as_ref().to_string_lossy().into_owned().as_str())?.as_ptr();

        unsafe {
            network
                .dsl_network
                .as_mut()
                .ReadFile(file_path, c_int(0), null_void_ptr)
                .into_result()?;
        }

        Ok(network)
    }

    /// Write the network to a file.
    ///
    /// The file type is determined automaticaly by the file_path extension.
    /// For the supported file types list, refer to the [smile::ffi::DSL_network::ReadFile].
    pub fn write_to_file(&mut self, file_path: impl AsRef<Path>) -> Result<()> {
        let null_void_ptr: *mut c_void = ptr::null_mut();

        let file_path =
            CString::new(file_path.as_ref().to_string_lossy().into_owned().as_str())?.as_ptr();

        unsafe {
            self.dsl_network
                .as_mut()
                .WriteFile(file_path, c_int(0), null_void_ptr)
                .into_result()
        }
    }

    /// Executes inference algorithm to update node values. Returns DSL_OKAY on success, or a negative error code on failure.
    pub fn update_beliefs(&mut self) -> Result<()> {
        self.dsl_network.as_mut().UpdateBeliefs().into_result()
    }

    /// Invalidates values in all nodes of the [Network].
    pub fn invalidate_all_beliefs(&mut self) -> Result<()> {
        self.dsl_network
            .as_mut()
            .InvalidateAllBeliefs()
            .into_result()
    }

    /// Sets the immediate update flag.
    ///
    /// If the flag is set to `true`, [Self::update_beliefs] will be called after changes in the network structure, parameters or evidence.
    pub fn is_update_immediate(&self) -> bool {
        self.dsl_network.as_ref().IsUpdateImmediate()
    }

    /// Returns the update immediate flag.
    ///
    /// For more information, refer to the [Self::is_update_immediate].
    pub fn set_update_immediate(&mut self, immediate: bool) {
        self.dsl_network.as_mut().SetUpdateImmediate(immediate);
    }

    /// Sets the algorithm used for inference in Bayesian networks.
    pub fn set_bayesian_network_algorithm(&mut self, algorithm: BayesianNetworkAlgorithm) {
        self.dsl_network
            .as_mut()
            .SetDefaultBNAlgorithm(algorithm.to_dsl_value());
    }

    /// Sets the algorithm used for inference in influence diagrams.
    pub fn set_influence_diagram_algorithm(&mut self, algorithm: InfluenceDiagramAlgorithm) {
        self.dsl_network
            .as_mut()
            .SetDefaultIDAlgorithm(algorithm.to_dsl_value());
    }

    // TODO: CalcProbEvidence
}

impl Clone for Network {
    /// Clone the network.
    /// ```rust
    /// # use nice_smile::network::Network;
    /// let network1 = Network::new_uninitialized();
    /// // TODO: add nodes to the network
    /// let network2 = network1.clone();
    /// assert!(network1 == network2);
    /// ```
    fn clone(&self) -> Self {
        let mut network = Self::new_uninitialized();
        network
            .dsl_network
            .as_mut()
            .Copy(&self.dsl_network, c_int(0));

        network
    }
}