rustml/
octave.rs

1extern crate num;
2
3use std::fmt;
4use std::fs::File;
5use std::io::{Write, Result};
6use std::process::{Command, Output};
7use std::iter::Iterator;
8
9use matrix::Matrix;
10
11static DEFAULT_OCTAVE_BIN: &'static str = "octave";
12
13pub struct OctaveScriptBuilder {
14    buf: Vec<String>,
15    octave_bin: String
16}
17
18impl OctaveScriptBuilder {
19    /// Adds the string to the Octave script.
20    ///
21    /// At the end of the line a semicolon is appended.
22    pub fn add(&self, s: &str) -> OctaveScriptBuilder {
23
24        let mut buf = self.buf.clone();
25        buf.push(s.to_string());
26        OctaveScriptBuilder {
27            buf: buf,
28            octave_bin: self.octave_bin.clone()
29        }
30    }
31
32    fn join<T: fmt::Display>(&self, v: &[T]) -> String {
33
34        let mut s = String::new();
35
36        for (idx, val) in v.iter().enumerate() {
37            if idx > 0 {
38                s = s + ",";
39            }
40            s = s + &format!("{}", val);
41        }
42        s
43    }
44
45    fn to_vec<T: fmt::Display>(&self, v: &[T]) -> String {
46
47        "[".to_string() + &self.join(v) + "]"
48    }
49
50    /// Adds the string to the Octave script.
51    ///
52    /// At the end of the line a semicolon is appended.
53    /// 
54    /// If the string contains a dollar sign followed by a number `i` (e.g. `$1` or `$12`)
55    /// this placeholder will be replaced by the column if matrix `m` at column
56    /// `i-1` (i.e. $1 is replaced by the first column of `m`).
57    ///
58    /// # Example
59    ///
60    /// ```
61    /// # #[macro_use] extern crate rustml;
62    /// use rustml::octave::*;
63    /// use rustml::matrix::Matrix;
64    ///
65    /// # pub fn main() {
66    /// let m = mat![
67    ///     1, 2, 3;
68    ///     4, 5, 6
69    /// ];
70    /// let s = builder().add_columns("x = $1; y = $2", &m);
71    /// assert_eq!(
72    ///     s.to_string(),
73    ///     "1;\nx = [1,4]; y = [2,5];\n"
74    /// );
75    /// # }
76    /// ```
77    pub fn add_columns<T: fmt::Display + Copy>(&self, s: &str, m: &Matrix<T>) -> OctaveScriptBuilder {
78
79        let mut t = s.to_string();
80        let n = m.cols();
81
82        for i in 0..n {
83            let p = format!("${}", i + 1);
84            let v = self.to_vec(&m.row_iter().map(|ref v| v[i]).collect::<Vec<T>>());
85            t = t.replace(&p, &v);
86        }
87        self.add(&t)
88    }
89
90    /// Adds the string to the Octave script.
91    ///
92    /// At the end of the line a semicolon is appended. If the string contains two
93    /// consecutive dollar signs (i.e. `$$`) these will be replaced by a vector
94    /// containing the elements of `vals`.
95    /// 
96    /// # Example
97    ///
98    /// ```
99    /// # extern crate rustml;
100    /// use rustml::octave::*;
101    ///
102    /// # pub fn main() {
103    /// let s = builder().add_vector("x = $$", &[1, 2, 3]);
104    /// assert_eq!(
105    ///     s.to_string(),
106    ///     "1;\nx = [1,2,3];\n"
107    /// );
108    /// # }
109    /// ```
110    pub fn add_vector<T: fmt::Display>(&self, s: &str, vals: &[T]) -> OctaveScriptBuilder {
111
112        let mut t = s.to_string();
113        let v = self.to_vec(vals);
114        t = t.replace("$$", &v);
115        self.add(&t)
116    }
117
118    /// Adds the string to the Octave script.
119    ///
120    /// At the end of the line a semicolon is appended. If the string contains two
121    /// consecutive dollar signs (i.e. `$$`) these will be replaced by a vector
122    /// containing the elements of iterator `vals`.
123    /// 
124    /// # Example
125    ///
126    /// ```
127    /// # extern crate rustml;
128    /// use rustml::octave::*;
129    ///
130    /// # pub fn main() {
131    /// let v = vec![1, 2, 3];
132    /// let s = builder().add_vector_iter("x = $$", v.iter());
133    /// assert_eq!(
134    ///     s.to_string(),
135    ///     "1;\nx = [1,2,3];\n"
136    /// );
137    /// # }
138    /// ```
139    pub fn add_vector_iter<T: fmt::Display, I: Iterator<Item = T>>(&self, s: &str, vals: I) -> OctaveScriptBuilder {
140
141        let v = vals.collect::<Vec<_>>();
142        self.add_vector(s, &v)
143    }
144
145    /// Adds the string to the Octave script.
146    ///
147    /// At the end of the line a semicolon is appended. If the string contains two
148    /// consecutive dollar signs (i.e. `$$`) these will be replaced by the matrix
149    /// `m`.
150    /// 
151    /// # Example
152    ///
153    /// ```
154    /// # #[macro_use] extern crate rustml;
155    /// use rustml::octave::*;
156    /// use rustml::*;
157    ///
158    /// # pub fn main() {
159    /// let m = mat![1, 2, 3; 4, 5, 6];
160    /// let s = builder().add_matrix("x = $$", &m);
161    /// assert_eq!(
162    ///     s.to_string(),
163    ///     "1;\nx = [1,2,3;4,5,6];\n"
164    /// );
165    /// # }
166    /// ```
167    pub fn add_matrix<T: fmt::Display + Clone>(&self, t: &str, m: &Matrix<T>) -> OctaveScriptBuilder {
168
169        let mut s = "[".to_string();
170
171        for (idx, r) in m.row_iter().enumerate() {
172            if idx > 0 {
173                s = s + ";";
174            }
175            s = s + &self.join(&r);
176        }
177        s = s + "]";
178
179        self.add(&t.replace("$$", &s))
180    }
181
182    /// Adds the string to the Octave script.
183    ///
184    /// At the end of the line a semicolon is appended.
185    ///
186    /// If the string contains a dollar sign followed by a number `i` (e.g. `$1` or `$12`)
187    /// this placeholder will be replaced by the value that is stored in the vector
188    /// `vals` at index `i-1`.
189    ///
190    /// # Example
191    ///
192    /// ```
193    /// # extern crate rustml;
194    /// use rustml::octave::*;
195    ///
196    /// # pub fn main() {
197    /// let s = builder().add_values("x = $1 + $2", &[5, 3]);
198    /// assert_eq!(
199    ///     s.to_string(),
200    ///     "1;\nx = 5 + 3;\n"
201    /// );
202    /// # }
203    /// ```
204    pub fn add_values<T: fmt::Display>(&self, s: &str, vals: &[T]) -> OctaveScriptBuilder {
205
206        let mut t = s.to_string();
207        let n = vals.len();
208
209        for i in 0..n {
210            let p = format!("${}", i + 1);
211            let v = format!("{}", vals[i]);
212            t = t.replace(&p, &v);
213        }
214        self.add(&t)
215    }
216
217    pub fn octave_bin(&self, path: &str) -> OctaveScriptBuilder {
218        OctaveScriptBuilder {
219            buf: self.buf.clone(),
220            octave_bin: path.to_string()
221        }
222    }
223
224    pub fn to_string(&self) -> String {
225        let mut s = String::new();
226        s = s + "1;\n";
227        for j in &self.buf {
228            s = s + &j + ";\n";
229        }
230        s
231    }
232
233    pub fn write(&self, filename: &str) -> Result<()> {
234
235        match File::create(filename) {
236            Ok(mut f) => {
237                let data = self.to_string().into_bytes();
238                f.write_all(&data)
239            },
240            Err(e) => Err(e)
241        }
242    }
243
244    pub fn run(&self, filename: &str) -> Result<Output> {
245
246        match self.write(filename) {
247            Ok(_) => {
248                let mut c = self.octave_bin.clone();
249                c = c + " " + filename;
250
251                Command::new("sh")
252                    .arg("-c")
253                    .arg(c)
254                    .output()
255            }
256            Err(e) => Err(e)
257        }
258    }
259}
260
261pub fn builder() -> OctaveScriptBuilder {
262    OctaveScriptBuilder {
263        buf: vec![],
264        octave_bin: DEFAULT_OCTAVE_BIN.to_string()
265    }
266}