time_table/
lib.rs

1// -!- rust -!- //////////////////////////////////////////////////////////////
2//
3//  System        : 
4//  Module        : 
5//  Object Name   : $RCSfile$
6//  Revision      : $Revision$
7//  Date          : $Date$
8//  Author        : $Author$
9//  Created By    : Robert Heller
10//  Created       : 2025-10-03 15:59:04
11//  Last Modified : <251025.1156>
12//
13//  Description	
14//
15//  Notes
16//
17//  History
18//	
19/////////////////////////////////////////////////////////////////////////////
20//    Copyright (C) 2025  Robert Heller D/B/A Deepwoods Software
21//			51 Locke Hill Road
22//			Wendell, MA 01379-9728
23//
24//    This program is free software; you can redistribute it and/or modify
25//    it under the terms of the GNU General Public License as published by
26//    the Free Software Foundation; either version 2 of the License, or
27//    (at your option) any later version.
28//
29//    This program is distributed in the hope that it will be useful,
30//    but WITHOUT ANY WARRANTY; without even the implied warranty of
31//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32//    GNU General Public License for more details.
33//
34//    You should have received a copy of the GNU General Public License
35//    along with this program; if not, write to the Free Software
36//    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
37// 
38//
39//////////////////////////////////////////////////////////////////////////////
40
41#![doc = include_str!("../README.md")]
42
43pub mod station;
44pub mod cab;
45pub mod train;
46pub mod primio;
47
48use crate::station::*;
49use crate::cab::*;
50use crate::train::*;
51use crate::primio::*;
52use std::collections::HashMap;
53//use std::collections::hash_map::Iter;
54//use std::collections::btree_map::Iter;
55//use std::slice::Iter;
56use std::collections::LinkedList;
57use std::path::PathBuf;
58use std::str::FromStr;
59use std::convert::Infallible;
60use std::fs::File;
61use std::io::prelude::*;
62use std::io::{Write,BufReader,BufWriter};
63use std::fmt;
64
65
66/**   A Vector of doubles.
67  * 
68  * Used as a vector of layover times.
69  *
70  *
71  */
72pub type DoubleVector = Vec<f64>;
73
74/**  Option hash map, used for Print options.
75  *
76  *
77  */
78
79pub type OptionHashMap = HashMap<String, String>;
80
81
82/**  List of trains.
83  *
84  * Simple linked list of trains, used for passing train lists
85  * around. 
86  *
87  *
88  */
89pub type TrainList<'latex> =  Vec<&'latex Train>;
90
91/**  Station times class, used by the LaTeX generator methods.
92  *
93  * This class holds time table information used in the code that generates 
94  * the LaTeX tables.  Each StationTimes item contains one table 
95  * element in the form of an arrival time and a departure time.  The flag
96  * member indicates if only the arrival time, departure time, or both times
97  * are valid.  An originating train has no arrival time and a terminating
98  * train has no departure time.
99  *
100  * This class is actually used to hold the information for a single cell in
101  * a formatted time table.  Each cell contains an arrivial time and a 
102  * departure time.  Each row in the table contains the information for a
103  * specific station and each column contains the information for a single
104  * train.
105  *
106  *
107  */
108#[derive(Debug, Clone, Copy, PartialEq)]
109pub struct StationTimes {
110    arrival: f64,
111    departure: f64,
112    flag: StopFlagType,
113}
114
115impl Default for StationTimes {
116    fn default() -> Self {
117        Self {arrival: -1.0, departure: -1.0, flag: StopFlagType::Transit}
118    }
119}
120        
121
122impl StationTimes {
123    /** Constructor: create an entry for a time table cell.
124      * ## Parameters:
125      * - a The arrival time.
126      * - d The departure time.
127      * - f The stop flag: Origin, Terminate, or Transit.
128      */
129    pub fn new(a: f64, d: f64, f: StopFlagType) -> Self {
130        Self {arrival: a, departure: d, flag: f }
131    }
132    /** Accessor for the arrival time.
133      */
134    pub fn Arrival(&self) -> f64 {self.arrival}
135    /** Accessor for the departure time.
136      */
137    pub fn Departure(&self) -> f64 {self.departure}
138    /** Accessor for the type of stop flag.
139      */
140    pub fn  Flag(&self) -> StopFlagType {self.flag}
141
142}
143
144/**  Map of station times, indexed by train number.
145  *
146  * These are the individual
147  * rows of the time table. The train number (symbol) is the column index.
148  * Each of these rows is for a single station.  This is a sparse vector, since
149  * not all trains stop at (or go past) all stations.  The ommited elements
150  * result in blank cells in the output table.
151  *
152  *
153  */
154pub type TrainStationTimes = HashMap<String,StationTimes>;
155
156/**  Map of maps of station times, indexed by station index.
157  *
158  * This is the whole
159  * time table.  The station index is the row index.  This is a sparse vector,
160  * since not all trains stop at (or go past) all stations.  The ommited
161  * elements result in blank cells in the output table.
162  *
163  *
164  */
165pub type TrainTimesAtStation = HashMap<usize, TrainStationTimes>;
166
167/**  List of strings.
168  *
169  * This is a simple linked list of strings, used in various places.
170  *
171  *
172  */
173pub type StringList =  LinkedList<String>;
174
175/**  Convert a list of strings to a flat string.
176  *
177  * The result is comma
178  * separated and each string is enclosed in quote characters 
179  * (__"__).  If a string contains a quote character or a
180  * backslash, the character is quoted with a backslash.
181  * ## Parameters:
182  * - list The list of strings.
183  *
184  *
185  */
186pub fn StringListToString(list: StringList) -> String {
187    let mut result: String = String::new();
188    let mut comma: String = String::new();
189    for theString in list.iter() {
190        result += &comma;
191        result += "\"";
192        for char in theString.chars() {
193            if char == '"' || char == '\\' {result += "\\";}
194            result += &String::from(char);
195        }
196        result += "\"";
197        comma = String::from(",");
198    }
199    result
200}
201
202/**  Convert a flat string to a list of strings.
203  *
204  * Returns false if there was a syntax error.
205  * ## Parameters:
206  * - strlinList The input string.
207  * - result The output list.
208  *
209  *
210  */
211pub fn StringListFromString(strlinList: String) -> Option<StringList> {
212    let mut result = LinkedList::new();
213    let mut inString: bool = false;
214    let mut expectcomma: bool = false;
215    let mut expectquotes: bool = true;
216    let mut escape: bool= false;
217    let mut theString: String = String::new();
218    for c in strlinList.chars() {
219        if inString {
220            if escape {
221                theString += &String::from(c);
222                escape = false;
223            } else if c == '"' {
224                result.push_back(theString.clone());
225                theString.clear();
226                inString = false;
227                expectcomma = true;
228                expectquotes = false;
229            } else if c == '\\' {
230                escape = true;
231            } else {
232                theString += &String::from(c);             
233            }
234        } else {
235            if c == ',' && expectcomma {
236                expectcomma = false;
237                expectquotes = true;
238            } else if c == '"' && expectquotes {
239                theString.clear();
240                inString = true; 
241            } else {
242                return None
243            }
244        }
245    }
246    if escape {
247        None
248    } else if result.len() == 0 && !inString && expectquotes {
249        Some(result)
250    } else if !inString && expectcomma {
251        Some(result)
252    } else {
253        None
254    }
255}
256
257/**  This is the main Time Table Class.
258  *
259  * It implements all of the basic data
260  * and algorithms used in the Time Table program.
261  *
262  * This class includes code to load a set of stations and the trains that
263  * run between these stations, along with code to read and write a time table
264  * file and code to create a formatted time table, suitable for printing (by
265  * way of LaTeX).
266  *
267  *
268  */
269
270#[derive(Debug, Clone, PartialEq)]
271pub struct TimeTableSystem {
272    name: String,
273    filepath: PathBuf,
274    timescale: u32,
275    timeinterval: u32,
276    stations: StationVector,
277    cabs: CabNameMap,
278    trains: TrainNumberMap,
279    notes: Vec<String>,
280    print_options: OptionHashMap,
281    table_of_contents_p: bool,
282    direction_name: String,
283}
284
285#[derive(Debug, Clone, PartialEq)]
286pub enum ConstructorError {
287    BadFilename(Infallible),
288    CouldNotOpenFile(String,String),
289    PrematureEOF(String),
290    FileIOError(String,String),
291    //MissingTimescale,
292    //TimescaleParseError(String,String),
293    //MissingTimeinterval,
294    //TimeintervalParseError(String,String),
295    //StationCountParseError(String,String),
296    //StationParseError(String,String),
297    //CabCountParseError(String,String),
298    //CabParseError(String,String),
299    //TrainCountParseError(String,String),
300    //TrainParseError(String,String),
301    //NoteCountParseError(String,String),
302    //OptionsCountParseError(String,String),
303}
304
305impl fmt::Display for ConstructorError {
306    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307        match self {
308            Self::BadFilename(patherr) => write!(f, "Pathname error: {}",patherr),
309            Self::CouldNotOpenFile(filename,error) =>
310                write!(f, "Cound not open file {}: {}",filename,error),
311            Self::PrematureEOF(where_) =>
312                write!(f, "Premature EOF {}",where_),
313            Self::FileIOError(where_,error) =>
314                write!(f, "File I/O Error: {}: {}",where_,error),
315            //Self::MissingTimescale =>
316            //    write!(f, "Timescale missing"),
317            //Self::TimescaleParseError(String,String) => ,
318            //Self::MissingTimeinterval =>
319            //    write!(f, "Timeinterval Missing"),
320            //Self::TimeintervalParseError(String,String),
321            //Self::StationCountParseError(String,String) => ,
322            //Self::StationParseError(String,String),
323            //Self::CabCountParseError(String,String),
324            //Self::CabParseError(String,String),
325            //Self::TrainCountParseError(String,String),
326            //Self::TrainParseError(String,String),
327            //Self::NoteCountParseError(String,String),
328            //Self::OptionsCountParseError(String,String),
329        }
330    }
331}
332
333#[derive(Debug, Clone, PartialEq)]
334pub enum AddTrainError {
335    EmptyStopList,
336    DuplicateTrain,
337    RangeError(String),
338    BadStationNumber(usize),
339    DuplicateStorageIsAt(String,String,String),
340    DuplicateStorageAt(String,String),
341
342}
343
344#[derive(Debug, Clone, PartialEq)] 
345pub enum DeleteTrainError {
346    NoSuchTrain(String),
347    InternalMissingOcc(String),
348}
349
350#[derive(Debug)] 
351pub enum CreateLaTeXError {
352    NoTrainsError,
353    FileIOError(std::io::Error),
354    GroupSyntaxError(u32),
355    GroupEmpty(u32),
356    
357}
358
359impl From<std::io::Error> for CreateLaTeXError {
360    fn from(error: std::io::Error) -> Self {
361        CreateLaTeXError::FileIOError(error)
362    }
363}
364
365#[derive(Debug, Clone, PartialEq)]
366pub enum GroupMode {
367    NoGrouping,
368    Class,
369}
370impl TimeTableSystem {
371    /**  The constructor that creates a time table system from an existing
372      * file.
373      *
374      * The file is read in and the class is properly initialized 
375      * from the data in the file.
376      * ## Parameters:
377      * - filename The name of the file to load.
378      */
379    pub fn old(filename: &str) -> Result<Self, ConstructorError> {
380        let filepath: PathBuf = match PathBuf::from_str(filename) {
381            Ok(f) => f,
382            Err(p) => return Err(ConstructorError::BadFilename(p)),
383        };
384        let file = match File::open(&filepath) {
385            Ok(f) => f,
386            Err(e) => return Err(ConstructorError::CouldNotOpenFile(
387                                                        filename.to_string(),
388                                                            e.to_string())),
389        };
390        let mut buf_reader = BufReader::new(file);
391        let mut name: String = String::new();
392        match buf_reader.read_line(&mut name) {
393            Ok(l) => if l == 0 {
394                        return Err(ConstructorError::PrematureEOF(String::from("Reading name")));
395                     },
396            Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading name"),e.to_string())),
397        };
398        name = name.trim().to_string();
399        let timescale: u32 = match ReadU32(&mut buf_reader) {
400            Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Timescale"),e.to_string())),
401            Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Timescale"))),
402            Ok(Some(s)) => s,
403        };
404        let timeinterval: u32 = match ReadU32(&mut buf_reader) {
405            Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Timeinterval"),e.to_string())),
406            Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Timeinterval"))),
407            Ok(Some(s)) => s,
408        };
409        let mut this = Self {name: name, filepath: filepath, 
410                             timescale: timescale, timeinterval: timeinterval,
411                             stations: Vec::new(), cabs: HashMap::new(), 
412                             trains: HashMap::new(), notes: Vec::new(), 
413                             print_options: HashMap::new(),
414                             table_of_contents_p: true, 
415                             direction_name: String::new() };
416        let mut count: usize = match ReadUSize(&mut buf_reader) {
417            Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Station count"),e.to_string())),
418            Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Station count"))),
419            Ok(Some(s)) => s,
420        };
421        for i in 0..count {
422            let station = match Station::Read(&mut buf_reader) {
423                Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Station"),e.to_string())),
424                Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Station"))),
425                Ok(Some(s)) => s,
426            };
427            this.stations.push(station);
428        }
429        count = match ReadUSize(&mut buf_reader) {
430            Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Cab count"),e.to_string())),
431            Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Cab count"))),
432            Ok(Some(s)) => s,
433        };
434        for i in 0..count {
435            let cab = match Cab::Read(&mut buf_reader) {
436                Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Cab"),e.to_string())),
437                Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Cab"))),
438                Ok(Some(c)) => c,
439            };
440            this.cabs.insert(cab.Name().clone(),cab);
441        }
442        count = match ReadUSize(&mut buf_reader) {
443            Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Train count"),e.to_string())),
444            Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Train count"))),
445            Ok(Some(s)) => s,
446        };
447        for i in 0..count {
448            let train = match Train::Read(&mut buf_reader,&this.cabs) {
449                Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Train"),e.to_string())),
450                Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Train"))),
451                Ok(Some(t)) => t,
452            };
453            this.trains.insert(train.Number(),train);
454        }
455        count = match ReadUSize(&mut buf_reader) {
456            Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Note count"),e.to_string())),
457            Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Note count"))),
458            Ok(Some(s)) => s,
459        };
460        for i in 0..count {
461            let note = match Self::ReadNote(&mut buf_reader) {
462                Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Note"),e.to_string())),
463                Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Note"))),
464                Ok(Some(n)) => n,
465            };
466            this.notes.push(note);
467        }
468        count = match ReadUSize(&mut buf_reader) {
469            Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Options count"),e.to_string())),
470            Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Options count"))),
471            Ok(Some(s)) => s,
472        };
473        for i in 0..count {
474            let optkey = match Self::ReadNote(&mut buf_reader) {
475                Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Options key"),e.to_string())),
476                Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Options key"))),
477                Ok(Some(n)) => n,
478            };
479            let optval = match Self::ReadNote(&mut buf_reader) {
480                Err(e) => return Err(ConstructorError::FileIOError(String::from("Reading Options value"),e.to_string())),
481                Ok(None) => return Err(ConstructorError::PrematureEOF(String::from("Reading Options value"))),
482                Ok(Some(n)) => n,
483            };
484            this.print_options.insert(optkey,optval);
485        }
486        Ok(this)
487    }
488    /**  The constructor that creates a new, empty time table system from
489      * stratch, given a set of esentual parameters.
490      * ## Parameters:
491      *  - name The name of the time table system.
492      *  - timescale Number of time units per 24 hours.  There are
493      *    1440 minutes in 24 hours.
494      *  - timeinterval The tick frequency in time units.
495      */
496    pub fn new(name: String,timescale: u32,timeinterval: u32) -> Self {
497        Self {name: name, filepath: PathBuf::new(), timescale: timescale, 
498              timeinterval: timeinterval, stations: Vec::new(), 
499              cabs: HashMap::new(), trains: HashMap::new(), notes: Vec::new(), 
500              print_options: HashMap::new(), table_of_contents_p: true, 
501              direction_name: String::new() }
502    }
503    /**  Add a new station to the system.
504      *
505      * Creates a new Station class
506      * instance and adds it to the station vector.  Stations must be
507      * added in order of their scale mile location.  If the new station
508      * is out of order, -1 is returned and the station is not added!
509      * ## Parameters:
510      *  - name The name of the station.
511      *  - smile The scale mile along the route where the station is
512      *    located.
513      */
514    pub fn AddStation(&mut self,name: String,smile: f64) -> Option<usize> {
515        match self.FindStationByName(name.clone()) {
516            None => match self.stations.last() {
517                        None => {
518                            self.stations.push(Station::new(name.clone(),smile));
519                            Some(self.stations.len()-1)
520                        },
521                        Some(l) =>
522                            if l.SMile() < smile {
523                                self.stations.push(Station::new(name.clone(),smile));
524                                Some(self.stations.len()-1)
525                            } else {
526                                None
527                            },
528                    },
529            Some(index) => Some(index),
530        }                        
531    }
532    /**  Find a station by name.
533      *
534      * Returns the index of the station or -1 if
535      * the station cannot be found.
536      * ## Parameters:
537      * - name The name of the station.
538     */
539    pub fn FindStationByName(&self,name: String) -> Option<usize> {
540        for i in 0..self.stations.len() {
541            if self.stations[i].Name() == name {
542                return Some(i);
543            }
544        }
545        None
546    }
547    /**  Number of stations. 
548      * 
549      * Returns the number of stations in the system.
550      */
551    pub fn NumberOfStations(&self) -> usize {self.stations.len()}
552    /**  Return Ith station object.
553      *
554      * Returns the NULL pointer if the index
555      * is out of range.
556      * ## Parameters:
557      * - i The index of the station.
558      */
559    pub fn IthStation(&self, i: usize) -> Option<&Station> {
560        self.stations.get(i)
561    }
562    pub fn IthStationMut(&mut self, i: usize) -> Option<&mut Station> {
563        self.stations.get_mut(i)
564    }
565    /**  Return the Ith station name. 
566      *
567      * Returns the NULL pointer if the index
568      * is out of range.
569      * ## Parameters:
570      * - i The index of the station.
571      */
572    pub fn StationName(&self,i: usize) -> Option<String> {
573        match self.stations.get(i) {
574            None => None,
575            Some(s) => Some(s.Name())
576        }
577    }
578    /**  Return the Ith station's scale mile location.
579      *
580      * Returns -1.0 if
581      * the index is out of range.
582      * ## Parameters:
583      * - i The index of the station.
584      */
585    pub fn SMile(&self,i: usize) -> Option<f64> {
586        match self.stations.get(i) {
587            None => None, 
588            Some(s) => Some(s.SMile()),
589        }
590    }
591    /**  The total length of the route in scale miles.  
592      * 
593      * This is just the
594      * scale mile location of the last station along the route.
595      */
596    pub fn TotalLength(&self) -> f64 {
597        if self.stations.len() == 0 {
598            0.0
599        } else {
600            self.stations.last().unwrap().SMile()
601        }
602    }
603    /**  The duplicate station index for a given station.  
604      * 
605      * Only meaningful
606      * for out and back type layouts or layouts that have shared trackage.
607      * This would be stations along shared trackage.  Returns -1 if
608      * the index is out of range or if there is not a duplicate station for
609      * the ith station.
610      * ## Parameters:
611      * - i The index of the station.
612      */
613    pub fn DuplicateStationIndex(&self,i: usize) -> Option<usize> {
614        match self.stations.get(i) {
615            None => None,
616            Some(s) => s.DuplicateStationIndex(),
617        }
618    }
619    /**  Set the duplicate station index for a given station.
620      *
621      * Only 
622      * meaningful for out and back type layouts or layouts that have 
623      * shared trackage. This would be stations along shared trackage.
624      * setting the duplicate station index indicates there is no
625      * duplicate station
626      * ## Parameters:
627      * - i The index of the station to be updated.
628      * - dup The other station index sharing this station 
629      *      location.
630      */
631    pub fn SetDuplicateStationIndex(&mut self,i: usize,dup: usize) {
632        match self.stations.get_mut(i) {
633            None => (),
634            Some(s) => s.SetDuplicateStationIndex(Some(dup)),
635        };
636    }
637    /**  Add a storage track to a station.  
638      *
639      * Sometimes stations, especially
640      * major terminals, have extra tracks for storing terminating and
641      * originating trains.  Returns the NULL pointer if the index is
642      * out of range.  Otherwise returns the pointer to the new 
643      * StorageTrack object.
644      * ## Parameters:
645      * - i The index of the station to be updated.
646      * - name The name for the new storage track.
647      */
648    pub fn AddStorageTrack(&mut self,i: usize,name: &String) -> Option<&mut StorageTrack> {
649        match self.stations.get_mut(i) {
650            None => None,
651            Some(s) => s.AddStorageTrack(name)
652        }
653    }
654    /**  Find a storage track at a station.
655      *
656      * Sometimes stations, especially
657      * major terminals, have extra tracks for storing terminating and
658      * originating trains. Returns the NULL pointer if the index is
659      * out of range or if there is no storage track with the specified
660      * name.  Otherwise the StorageTrack object pointer is returned.
661      * ## Parameters:
662      * - i The index of the station to be updated.
663      * - name The name of the storage track.
664      */
665    pub fn FindStorageTrack(&self,i: usize,name: &String) -> Option<&StorageTrack> {
666        match self.stations.get(i) {
667            None => None,
668            Some(s) => s.FindStorageTrack(name),
669        }
670    }
671    pub fn FindStorageTrack_mut(&mut self,i: usize,name: &String) -> Option<&mut StorageTrack> {
672        match self.stations.get_mut(i) {
673            None => None,
674            Some(s) => s.FindStorageTrack_mut(name),
675        }
676    }
677    /**  Add a new cab to the system.
678      *
679      * With DC systems this would be an
680      * actual cab.  With DCC systems, this can be used to define a
681      * logical operator for the train.  The color is used for visual
682      * distintion.  A pointer to the new cab object is returned.
683      * ## Parameters:
684      * - name The name of the cab.
685      * - color The color of the cab.
686      */
687    pub fn AddCab(&mut self,name: String,color: String) -> &Cab {
688        self.cabs.entry(name.clone()).or_insert(Cab::new(name.clone(),color))
689    }
690    /**  Find a cab (by name).  
691      *
692      * Returns the pointer to the named cab or NULL
693      * if the cab was not found.
694      * ## Parameters:
695      * - name The cab name to look for.
696      */
697    pub fn FindCab(&self,name: &String) -> Option<&Cab> {
698        self.cabs.get(name)
699    }
700    /**  The nymber of cabs.
701      */
702    pub fn NumberOfCabs(&self) -> usize {self.cabs.len()}
703    /**  Add a train to the system, short version.  
704      *
705      * Creates a new Train
706      * opject and adds it to the train map.  The short version assumes
707      * that the train does not layover at any of the stops.  Layover
708      * times can be added later.  Returns a pointer to the new Train
709      * object.
710      * ## Parameters:
711      * - name The name of the train.
712      * - number The number (or symbol) of the train.
713      * - speed The trains maximum speed.
714      * - classnumber The class (inverse priority) of the train.
715      * - departure The train's departure time.
716      * - start The train's origin station index.  Defaults to the
717      *    first station.
718      * - end The train's destination station index. Defaults to
719      *    the last station.
720      */
721    pub fn AddTrain(&mut self,name: String,number: String,speed: u32, 
722                    classnumber: u32, departure: u32, start: usize, 
723                    iend: isize) -> Result<&Train,AddTrainError> {
724        let mut end: usize;
725        if iend < 0 {
726            end = self.NumberOfStations()-1;
727            if start == end {end = 0;}
728        } else {
729            end = iend as usize;
730        }
731        if start == end {
732            Err(AddTrainError::EmptyStopList)
733        } else {
734            let startsmile = self.stations[start].SMile();
735            let newTrain = Train::new(name,number.clone(),speed,classnumber,departure,startsmile,start,end);
736            Ok(self.trains.entry(number).or_insert(newTrain))
737        }
738    }
739    /**  Add a train to the system, long version (includes storage track
740      * checking).  
741      *
742      * This version includes layover times, cabnames, and
743      * storage track assignments.  Returns a pointer to the new Train
744      * object or the NULL pointer if there was an error, in which case
745      * the error message will be stored in the pointer provided.
746      * ## Parameters:
747      * - name The name of the train.
748      * - number The number (or symbol) of the train.
749      * - speed The trains maximum speed.
750      * - classnumber The class (inverse priority) of the train.
751      * - departure The train's departure time.
752      * - start The train's origin station index.
753      * - end The train's destination station index.
754      * - layoverVector The train's layover vector.
755      * - cabnameVector The train's departure cab name vector.
756      * - storageTrackVector The train's storage track name vector.
757      */
758    pub fn AddTrainLongVersion(&mut self,name: String,number: String,
759                               speed: u32,classnumber: u32,departure: u32,
760                               start: usize,end: usize, 
761                               layoverVector: &DoubleVector,
762                               cabnameVector: &StringList,
763                               storageTrackVector: &StringList) 
764                -> Result<&Train,AddTrainError> {
765        match self.FindTrainByNumber(&number) {
766            /*----------------------------------------------------------
767             * Duplicate train check.
768             *----------------------------------------------------------*/
769            Some(t) => return Err(AddTrainError::DuplicateTrain),
770            None => {
771                /*----------------------------------------------------------
772                 * Empty stop list  check.
773                 *----------------------------------------------------------*/
774                if start == end {
775                    return Err(AddTrainError::EmptyStopList)
776                } else {
777                    /*-----------------------------------------------------------
778                     * Storage track occupancy check.  Traverse the train's 
779                     * travel, making sure the storage tracks it will use are
780                     * available.
781                     *-----------------------------------------------------------*/
782
783                    let incr: bool;
784                    let nstops: usize;
785                    if start < end {
786                        incr = true;
787                        nstops = (end-start)+1;
788                    } else {
789                        incr = false;
790                        nstops = (start-end)+1;
791                    }
792                    /*----------------------------------------------------------
793                     * Range check: make sure the layover, cabname, and storage track
794                     * vectors are the right size
795                     *----------------------------------------------------------*/
796                    if layoverVector.len() != nstops ||
797                       cabnameVector.len() != nstops ||
798                       storageTrackVector.len() != nstops {
799                        return Err(AddTrainError::RangeError(
800                            if layoverVector.len() != nstops {
801                                String::from("layover vector")
802                            } else if cabnameVector.len() != nstops {
803                                String::from("cabname vector")
804                            } else if storageTrackVector.len() != nstops {
805                                String::from("storage track vector")
806                            } else {
807                                String::new()
808                            }));
809                    }
810                    // for (istop = start,i=0; i < nstops; istop += inxt,i++) {
811                    let mut istop: usize = start;
812                    let mut layoverIter = layoverVector.iter();
813                    //let mut cabnameIter = cabnameVector.iter();
814                    let mut storageTrackIter = storageTrackVector.iter();
815                    let mut oldDepart: f64 = -1.0;
816                    let mut oldSmile:  f64 = -1.0;
817                    for i in 0..nstops {
818                        let layover = *layoverIter.next().unwrap();
819                        let station = match self.IthStation(istop) {
820                            Some(s) => s,
821                            None => 
822                                return Err(AddTrainError::BadStationNumber(istop)),
823                        };
824                        let smile = station.SMile();
825                        let arrival: f64;
826                        if oldDepart >= 0.0 {
827                            arrival = oldDepart + 
828                                ((smile - oldSmile).abs() * (speed as f64 / 60.0));
829                        } else {
830                            arrival = departure as f64;
831                        }
832                        let depart = arrival + layover;
833                        oldDepart = depart;
834                        oldSmile  = smile; 
835                        let storageTrackName: &String = storageTrackIter.next().unwrap();
836                        if storageTrackName.len()> 0 {
837                            let storage = station.FindStorageTrack(storageTrackName);
838                            let rStation = match station.DuplicateStationIndex() {
839                                None => None,
840                                Some(rStationIndex) =>
841                                    self.IthStation(rStationIndex),
842                            };
843                            let rStorage = match rStation {
844                                None => None,
845                                Some(rs) => rs.FindStorageTrack(storageTrackName),
846                            };
847                            if istop == start {
848                                match storage {
849                                    None => (),
850                                    Some(s) => match s.IncludesTime(departure as f64) {
851                                        None => (),
852                                        Some(o) => {
853                                            let tn2 = o.TrainNum2();
854                                            if tn2.len() > 0 {
855                                                return Err(AddTrainError::DuplicateStorageIsAt(storageTrackName.to_string(),tn2,station.Name()));
856                                            }
857                                        }
858                                    }
859                                };
860                                match rStorage {
861                                    None => (),
862                                    Some(s) => match s.IncludesTime(departure as f64) {
863                                        None => (),
864                                        Some(o) => {
865                                            let tn2 = o.TrainNum2();
866                                            if tn2.len() > 0 {
867                                                return Err(AddTrainError::DuplicateStorageIsAt(storageTrackName.to_string(),tn2,rStation.unwrap().Name()));
868                                            }
869                                        },
870                                    },
871                                };
872                            } else if istop == end {
873                                match storage {
874                                    None => (),
875                                    Some(s) => match s.IncludesTime(arrival) {
876                                        None => (),
877                                        Some(o) => {
878                                            let tn = o.TrainNum();
879                                            if tn.len() > 0 {
880                                                return Err(AddTrainError::DuplicateStorageIsAt(storageTrackName.to_string(),tn,station.Name()));
881                                            }
882                                        }
883                                    }
884                                };
885                                match rStorage {
886                                    None => (),
887                                    Some(s) => match s.IncludesTime(arrival) {
888                                        None => (),
889                                        Some(o) => {
890                                            let tn = o.TrainNum();
891                                            if tn.len() > 0 {
892                                                return Err(AddTrainError::DuplicateStorageIsAt(storageTrackName.to_string(),tn,rStation.unwrap().Name()));
893                                            }
894                                        }
895                                    }
896                                };
897                            } else if layover > 0.0 && storage.is_some() {
898                                let o1 = storage.unwrap().IncludesTime(arrival);
899                                let o2 = storage.unwrap().IncludesTime(depart);
900                                if o1.is_some() || o2.is_some() {
901                                    return Err(AddTrainError::DuplicateStorageAt(storageTrackName.to_string(),station.Name()));
902                                }
903                                match rStorage {
904                                    None => (),
905                                    Some(rs) => {
906                                        let o1 = rs.IncludesTime(arrival);
907                                        let o2 = rs.IncludesTime(depart);
908                                        if o1.is_some() || o2.is_some() {
909                                            return Err(AddTrainError::DuplicateStorageAt(storageTrackName.to_string(),rStation.unwrap().Name()));
910                                        }
911                                    },
912                                };
913                            }
914                        }
915                        if incr {
916                            istop += 1;
917                        } else {
918                            istop -= 1;
919                        }
920                    }                    
921                    /*-------------------------------------------------------------
922                     * Create and store the train.
923                     *-------------------------------------------------------------*/
924                    let startsmile = self.stations[start].SMile();
925                    let mut newTrain = Train::new(name,number.clone(),speed,classnumber,departure,startsmile,start,end);
926                    /*-------------------------------------------------------------
927                     * Process the layovers, cabnames, and storage tracks.
928                     *-------------------------------------------------------------*/
929                    // for (istop = start,i=0; i < nstops; istop += inxt,i++) {
930                    let mut istop: usize = start;
931                    let mut layoverIter = layoverVector.iter();
932                    let mut cabnameIter = cabnameVector.iter();
933                    let mut storageTrackIter = storageTrackVector.iter();
934                    let mut oldDepart: f64 = -1.0;
935                    let mut oldSmile:  f64 = -1.0;
936                    for i in 0..nstops {
937                        let layover = *layoverIter.next().unwrap();
938                        newTrain.UpdateStopLayover(istop,layover);
939                        let cabName = cabnameIter.next().unwrap();
940                        if cabName.len() > 0 {
941                            let cab = self.FindCab(cabName).unwrap();
942                            newTrain.UpdateStopCab(istop,Some(cab));
943                        } else {
944                            newTrain.UpdateStopCab(istop,None);
945                        }
946                        let storageTrackName = storageTrackIter.next().unwrap();
947                        let station = self.IthStation(istop).unwrap();
948                        let smile = station.SMile();
949                        let arrival: f64;
950                        if oldDepart >= 0.0 {
951                            arrival = oldDepart + ((smile - oldSmile).abs() * (speed as f64 / 60.0));
952                        } else {
953                            arrival = departure as f64;
954                        }
955                        let depart = arrival + layover;
956                        oldDepart = depart;
957                        oldSmile  = smile;
958                        if storageTrackName.len() > 0 {
959                            Self::UpdateTrainStorageAtStop(istop,start,end,
960                                                           &mut self.stations,
961                                                           storageTrackName,
962                                                           arrival,depart,
963                                                           layover,
964                                                           self.timescale as f64,
965                                                           &mut newTrain);
966                        }
967                        if incr {
968                            istop += 1;
969                        } else {
970                            istop -= 1;
971                        }
972                    }
973                    Ok(self.trains.entry(number).or_insert(newTrain))
974                }
975            },
976        }
977    }
978    fn UpdateTrainStorageAtStop(istop: usize, start: usize, end: usize,
979                                stations: &mut StationVector,
980                                storageTrackName: &String,
981                                arrival: f64, depart: f64,
982                                layover: f64,infi: f64,
983                                newTrain: &mut Train) {
984        let (station, rStation) = match stations[istop].DuplicateStationIndex() {
985            None => (&mut stations[istop], None),
986            Some(rsI) => stations
987                .get_disjoint_mut([istop, rsI])
988                .map(|[s, r]| (s, Some(r)))
989                .unwrap(),
990        };
991        let storage  = station.FindStorageTrack_mut(storageTrackName);
992        let rStorage = match rStation {
993            None => None,
994            Some(rs) => rs.FindStorageTrack_mut(storageTrackName),
995        };
996        if istop == start {
997            match storage {
998                None => (),
999                Some(storage) => {
1000                    newTrain.SetOriginStorageTrack(storageTrackName.to_string());
1001                    let occupied = storage.IncludesTime(arrival);
1002                    match occupied {
1003                        None => {
1004                            storage.StoreTrain(String::new(),0.0,arrival,newTrain.Number());
1005                        },
1006                        Some(occupied) => {
1007                            let from = occupied.From();
1008                            let to   = occupied.Until();
1009                            storage.UpdateStoredTrain2(from,to,newTrain.Number());
1010                            storage.UpdateStoredTrainDeparture(from,to,arrival);
1011                        },
1012                    }
1013                },
1014            }
1015            match rStorage {
1016                None => (),
1017                Some(rStorage) => {
1018                    let occupied = rStorage.IncludesTime(arrival);
1019                    match occupied {
1020                        None => {
1021                            rStorage.StoreTrain(String::new(),0.0,arrival,
1022                                                newTrain.Number());
1023                        },
1024                        Some(occupied) => {
1025                            let from = occupied.From();
1026                            let to   = occupied.Until();
1027                            rStorage.UpdateStoredTrain2(from,to,newTrain.Number());
1028                            rStorage.UpdateStoredTrainDeparture(from,to,arrival);
1029                        }
1030                    }
1031                }
1032            }
1033        } else if istop == end {
1034            match storage {
1035                None => (),
1036                Some(storage) => {
1037                    newTrain.SetDestinationStorageTrack(storageTrackName.to_string());
1038                    let occupied = storage.IncludesTime(arrival);
1039                    match occupied {
1040                        None => {
1041                            storage.StoreTrain(newTrain.Number(),arrival,infi,String::new());
1042                        },
1043                        Some(occupied) => {
1044                            let from = occupied.From();
1045                            let to   = occupied.Until();
1046                            storage.UpdateStoredTrain(from,to,newTrain.Number());
1047                            storage.UpdateStoredTrainArrival(from,to,arrival);
1048                        },
1049                    };
1050                    match rStorage {
1051                        None => (),
1052                        Some(rStorage) => {
1053                            let occupied = rStorage.IncludesTime(arrival);
1054                            match occupied {
1055                                None => {
1056                                    rStorage.StoreTrain(newTrain.Number(),arrival,infi,String::new());
1057                                },
1058                                Some(occupied) => {
1059                                    let from = occupied.From();
1060                                    let to   = occupied.Until();
1061                                    rStorage.UpdateStoredTrain(from,to,newTrain.Number());
1062                                    rStorage.UpdateStoredTrainArrival(from,to,arrival);
1063                                },
1064                            }
1065                        }
1066                    };
1067                },
1068            };
1069        } else if layover > 0.0 && storage.is_some() {
1070            newTrain.SetTransitStorageTrack(istop,storageTrackName.to_string());
1071            let storage = storage.unwrap();
1072            storage.StoreTrain(newTrain.Number(),arrival,depart,newTrain.Number());
1073            match rStorage {
1074                None => (),
1075                Some(rStorage) => {
1076                    rStorage.StoreTrain(newTrain.Number(),arrival,depart,newTrain.Number());
1077                },
1078            };
1079        }
1080    }
1081    /**
1082      *  Delete a train.  
1083      *
1084      * Returns true if the train was successfully deleted
1085      * and false if not.  If the train was not deleted, an error message
1086      * will be provided in the pointer provided.
1087      * ## Parameters:
1088      * - number The train number or symbol.
1089      */
1090    pub fn DeleteTrain(&mut self,number: String) -> Result<(),DeleteTrainError> {
1091        match self.trains.get_mut(&number) {
1092            None => Err(DeleteTrainError::NoSuchTrain(number.clone())),
1093            Some(oldTrain) => {
1094                /*-----------------------------------------------------------
1095                 * Storage track occupancy cleanup.
1096                 *-----------------------------------------------------------*/
1097                let departure = oldTrain.Departure();
1098                let speed     = oldTrain.Speed();
1099                let mut oldDepart: f64 = -1.0;
1100                let mut oldSmile: f64 = -1.0;
1101                for istop in 0..oldTrain.NumberOfStops() {
1102                    let stop = oldTrain.StopI(istop).unwrap();
1103                    let istation = stop.StationIndex();
1104                    let (station, mut rStation) = match self.stations[istation].DuplicateStationIndex() {
1105                        None => (&mut self.stations[istation], None),
1106                        Some(rsI) => self.stations
1107                            .get_disjoint_mut([istation, rsI])
1108                            .map(|[s, r]| (s, Some(r)))
1109                            .unwrap(),
1110                    };
1111                    let layover = stop.Layover();
1112                    let smile   = station.SMile();
1113                    let arrival: f64;
1114                    if oldDepart < 0.0 {
1115                        arrival = departure as f64;
1116                    } else {
1117                        arrival = oldDepart + ((smile - oldSmile) * (speed as f64 / 60.0));
1118                    }
1119                    let depart: f64 = arrival + layover;
1120                    oldDepart = depart;
1121                    oldSmile  = smile;
1122                    let storageTrackName = stop.StorageTrackName();
1123                    if storageTrackName.len() == 0 {continue;}
1124                    let storage = station.FindStorageTrack_mut(&storageTrackName);
1125                    let rStorage = match rStation {
1126                        None => None,
1127                        Some(ref mut rs) => rs.FindStorageTrack_mut(&storageTrackName),
1128                    };
1129                    match stop.Flag() {
1130                        StopFlagType::Origin => {
1131                            match storage {
1132                                None => {
1133                                  return Err(DeleteTrainError::InternalMissingOcc(station.Name()));
1134                                },
1135                                Some(storage) => { 
1136                                    let occupied = storage.IncludesTime(departure as f64);
1137                                    match occupied {
1138                                        None => {
1139                                            return Err(DeleteTrainError::InternalMissingOcc(station.Name()));
1140                                        },
1141                                        Some(occupied) => {
1142                                            if occupied.From() == occupied.Until() &&
1143                                               occupied.From() == departure as f64 &&
1144                                               occupied.TrainNum() == number &&
1145                                               occupied.TrainNum2() == number {
1146                                                storage.RemovedStoredTrain(occupied.From(),occupied.Until());
1147                                            } else {
1148                                                let from = occupied.From();
1149                                                let to   = occupied.Until();
1150                                                storage.UpdateStoredTrain2(from,to,occupied.TrainNum());
1151                                                storage.UpdateStoredTrainDeparture(from,to,from);
1152                                             }
1153                                        },
1154                                    }
1155                                }
1156                            };
1157                            match rStorage {
1158                                None => (),
1159                                Some(rStorage) => {
1160                                    let occupied = rStorage.IncludesTime(departure as f64);
1161                                    match occupied {
1162                                        None => {
1163                                            return Err(DeleteTrainError::InternalMissingOcc(rStation.unwrap().Name()));
1164                                        },
1165                                        Some(occupied) => {
1166                                            if occupied.From() == occupied.Until() &&
1167                                               occupied.From() == departure as f64 &&
1168                                               occupied.TrainNum() == number &&
1169                                               occupied.TrainNum2() == number {
1170                                                rStorage.RemovedStoredTrain(occupied.From(),occupied.Until());
1171                                            } else {
1172                                                let from = occupied.From();
1173                                                let to   = occupied.Until();
1174                                                rStorage.UpdateStoredTrain2(from,to,occupied.TrainNum());
1175                                                rStorage.UpdateStoredTrainDeparture(from,to,from);
1176                                             }
1177                                        },
1178                                    };
1179                                },
1180                            };
1181                        },
1182                        StopFlagType::Terminate => {
1183                            match storage {
1184                                None => {
1185                                    return Err(DeleteTrainError::InternalMissingOcc(station.Name()));
1186                                },
1187                                Some(storage) => {
1188                                    let occupied = storage.IncludesTime(arrival);
1189                                    match occupied {
1190                                        None => {
1191                                            return Err(DeleteTrainError::InternalMissingOcc(station.Name()));
1192                                        },
1193                                        Some(occupied) => {
1194                                            if occupied.From() == occupied.Until() &&
1195                                               occupied.From() == arrival &&
1196                                               occupied.TrainNum() == number &&
1197                                               occupied.TrainNum2() == number {
1198                                                storage.RemovedStoredTrain(occupied.From(),occupied.Until());
1199                                            } else {
1200                                                let from = occupied.From();
1201                                                let to   = occupied.Until();
1202                                                storage.UpdateStoredTrain(from,to,occupied.TrainNum());
1203                                                storage.UpdateStoredTrainArrival(from,to,to);
1204                                             }
1205                                        },
1206                                    };
1207                                },
1208                            };
1209                            match rStorage {
1210                                None => (),
1211                                Some(rStorage) => {
1212                                    let occupied = rStorage.IncludesTime(arrival);
1213                                    match occupied {
1214                                        None => {
1215                                            return Err(DeleteTrainError::InternalMissingOcc(rStation.unwrap().Name()));
1216                                        },
1217                                        Some(occupied) => {
1218                                            if occupied.From() == occupied.Until() &&
1219                                               occupied.From() == arrival &&
1220                                               occupied.TrainNum() == number &&
1221                                               occupied.TrainNum2() == number {
1222                                                rStorage.RemovedStoredTrain(occupied.From(),occupied.Until());
1223                                            } else {
1224                                                let from = occupied.From();
1225                                                let to   = occupied.Until();
1226                                                rStorage.UpdateStoredTrain(from,to,occupied.TrainNum());
1227                                                rStorage.UpdateStoredTrainArrival(from,to,to);
1228                                             }
1229                                        },
1230                                    };
1231                                },
1232                            };
1233                        },
1234                        StopFlagType::Transit => {
1235                            if layover > 0.0 && storage.is_some() {
1236                                let storage = storage.unwrap();
1237                                let o1 = storage.IncludesTime(arrival);
1238                                let o2 = storage.IncludesTime(depart);
1239                                if o1 != o2 || o1.is_none() || o2.is_none() {
1240                                    return Err(DeleteTrainError::InternalMissingOcc(station.Name()));
1241                                } else {
1242                                    let o1 = o1.unwrap();
1243                                    let o2 = o2.unwrap();
1244                                    storage.RemovedStoredTrain(o1.From(),o1.Until());
1245                                }
1246                            }
1247                            if layover > 0.0 && rStorage.is_some() {
1248                                let rStorage = rStorage.unwrap();
1249                                let o1 = rStorage.IncludesTime(arrival);
1250                                let o2 = rStorage.IncludesTime(depart);
1251                                if o1 != o2 || o1.is_none() || o2.is_none() {
1252                                    return Err(DeleteTrainError::InternalMissingOcc(rStation.unwrap().Name()));
1253                                } else {
1254                                    let o1 = o1.unwrap();
1255                                    let o2 = o2.unwrap();
1256                                    rStorage.RemovedStoredTrain(o1.From(),o1.Until());
1257                                }
1258                            }
1259                        },
1260                    };
1261                }
1262                /*
1263                 * Remove the train from the map.
1264                 */
1265                self.trains.remove(&number);
1266                /*
1267                 * Delete the train.
1268                 */
1269                /* (no pointers to free in rust...) */
1270                Ok(())
1271            }
1272        }
1273    }
1274    /**  Find a train by name.
1275      *
1276      * Returns the pointer to the named train or
1277      * NULL if the train was not found.
1278      * ## Parameters:
1279      * - name The train name to look for.
1280      */
1281    pub fn FindTrainByName(&self,name: &String) -> Option<&Train> {
1282        for train in self.trains.values() {
1283            if *train.Name() == *name {
1284                return Some(train);
1285            }
1286        }
1287        None
1288    }
1289    /**  Find a train by number (or symbol). 
1290      *
1291      * Returns the pointer to the 
1292      * train or NULL if the train was not found.
1293      * ## Parameters:
1294      * - number The train number (or symbol) to look for.
1295      */
1296    pub fn FindTrainByNumber(&self, number: &String) -> Option<&Train> {
1297        self.trains.get(number)
1298    }
1299    /**  Return the number of trains.
1300      */
1301    pub fn NumberOfTrains(&self) -> usize {self.trains.len()}
1302    /**  Return the number of notes.
1303      */
1304    pub fn NumberOfNotes(&self)  -> usize {self.notes.len()}
1305    /**  Return the ith note (1-based!) as a string. , 
1306      * Returns the NULL
1307      * pointer if the index is out of range.
1308      * ## Parameters:
1309      * - i The note index.  The first note is at index 1, not 0!.
1310      */
1311    pub fn Note(&self,i: usize) -> Option<String> {
1312        self.notes.get(i).cloned()
1313    }
1314    /**  Add a note to the notes vector.
1315      * ## Parameters:
1316      * - newnote The text of the new note.
1317      */
1318    pub fn AddNote(&mut self,newnote: String) -> usize {
1319        self.notes.push(newnote);
1320        self.notes.len()
1321    }
1322    /**  Set the ith note (1-based!).  
1323      *
1324      * Updates the text of the specificed
1325      * note.  Returns true if the note was updated or false if the
1326      * index was out of range.
1327      * ## Parameters:
1328      * - i The note index.  The first note is at index 1, not 0!.
1329      * - note The new text for the note.
1330      */
1331    pub fn  SetNote(&mut self,i: usize,note: String) -> bool {
1332        if i == 0 || i > self.notes.len() {
1333            false
1334        } else {
1335            self.notes[i-1] = note;
1336            true
1337        }
1338    }
1339    /**  Fetch a print option.  
1340      *
1341      * Returns the value of a specified print
1342      * option or the empty string if the print option was not found.
1343      * ## Parameters:
1344      * - key The name of the print option.
1345      */
1346    pub fn GetPrintOption(&self,key: &str) -> Option<&String>
1347    {
1348        self.print_options.get(key)
1349    }
1350    /**  Set a print option.  
1351      *
1352      * Sets the value of a print option.  Creates a
1353      * new hash table element if the specified print option does not
1354      * already exist.
1355      * ## Parameters:
1356      * - key The name of the print option to be set.
1357      * - value The value to set the print option to.
1358      */
1359    pub fn SetPrintOption(&mut self,key: &str,value: &str) {
1360        self.print_options.insert(key.to_string(),value.to_string());
1361    }
1362    /**  Write out a Time Table System to a new file.  
1363      *
1364      * The current contents
1365      * of the time table is written to a new time table file. Returns 
1366      * true if successful and false if not.
1367      * ## Parameters:
1368      * - filename The name of the file to write (if empty, use
1369      *    existing name, if available).
1370      * - setfilename Change the filename if true.
1371      * 
1372      */
1373    pub fn WriteNewTimeTableFile(&mut self,filename: &String, 
1374                                 setfilename: bool) -> std::io::Result<()> {
1375        let out = File::create(filename)?;
1376        let mut fp =  BufWriter::new(out);
1377        writeln!(fp,"{}",self.name)?;
1378        writeln!(fp,"{} {}",self.timescale,self.timeinterval)?;
1379        writeln!(fp,"{}",self.stations.len())?;
1380        for s in self.stations.iter() {
1381            s.Write(&mut fp)?;
1382        }
1383        writeln!(fp,"{}",self.cabs.len())?;
1384        for c in self.cabs.values() {
1385            c.Write(&mut fp)?;
1386        }
1387        writeln!(fp,"{}",self.trains.len())?;
1388        for t in self.trains.values() {
1389            t.Write(&mut fp)?;
1390        }
1391        writeln!(fp,"{}",self.notes.len())?;
1392        for note in self.notes.iter() {
1393            Self::WriteNote(&mut fp,note)?;
1394            writeln!(fp,"")?;
1395        }
1396        writeln!(fp,"{}",self.print_options.len())?;
1397        for (opt,val) in self.print_options.iter() {
1398            Self::WriteNote(&mut fp,opt)?;
1399            write!(fp," ")?;
1400            Self::WriteNote(&mut fp,val)?;
1401            writeln!(fp,"")?;
1402        }
1403        if setfilename {
1404            self.filepath = PathBuf::from_str(&filename).unwrap();
1405        }
1406        Ok(())
1407    }
1408    /**  Return time scale.
1409      */
1410    pub fn TimeScale(&self) -> u32 {self.timescale}
1411    /**  Return time interval.
1412      */
1413    pub fn TimeInterval(&self) -> u32 {self.timeinterval}
1414    /**  Return the name of the system.
1415      */
1416    pub fn Name(&self) -> String {self.name.clone()}
1417    /**  Return file pathname.
1418      */
1419    pub fn Filename(&self) -> String {self.filepath.display().to_string()}
1420    pub fn StationsIter(&self) -> std::slice::Iter<'_, Station> {
1421        self.stations.iter()
1422    }
1423    pub fn StationsIter_mut(&mut self) -> std::slice::IterMut<'_, Station> {
1424        self.stations.iter_mut()
1425    }
1426    pub fn CabsIter(&self) -> std::collections::hash_map::Iter<'_, String, Cab> {
1427        self.cabs.iter()
1428    }
1429    pub fn CabsIter_mut(&mut self) -> std::collections::hash_map::IterMut<'_, String, Cab> {
1430        self.cabs.iter_mut()
1431    }
1432    pub fn TrainsIter(&self) -> std::collections::hash_map::Iter<'_, String, Train> {
1433        self.trains.iter()
1434    }
1435    pub fn TrainsIter_mut(&mut self) -> std::collections::hash_map::IterMut<'_, String, Train> {
1436        self.trains.iter_mut()
1437    }
1438    pub fn NotesIter(&self) -> std::slice::Iter<'_, String> {
1439        self.notes.iter()
1440    }
1441    pub fn NotesIter_mut(&mut self) -> std::slice::IterMut<'_, String> {
1442        self.notes.iter_mut()
1443    }
1444    pub fn PrintOptionsIter(&self) -> std::collections::hash_map::Iter<'_, String, String> {
1445        self.print_options.iter()
1446    }
1447    pub fn PrintOptionsIter_mut(&mut self) -> std::collections::hash_map::IterMut<'_, String, String> {
1448        self.print_options.iter_mut()
1449    }
1450    /*
1451     * Common LaTeX characters.
1452     */
1453    const BACKSLASH: char = '\\';
1454    const OPENBRACE: char = '{';
1455    const CLOSEBRACE: char = '}';
1456    /**  Create a LaTeX file for generating a (hard copy) Employee
1457      * Timetable. 
1458      *
1459      * This method create a LaTeX source file from
1460      * the information in the time table structure.  It access various
1461      * print options to control how the LaTeX file is generated.
1462      *
1463      * The LaTeX file generated depends on a LaTeX style file named 
1464      * `TimeTable.sty` (there is a version of this file in the `src/latex` 
1465      * directory).  This file needs to be in the same directory as the 
1466      * generated LaTeX file this method generates.
1467      *
1468      * ## Parameters:
1469      * - filename The name of the  LaTeX file to create.
1470      */
1471    pub fn CreateLaTeXTimetable(&mut self,filename: &str) 
1472            -> Result<(),CreateLaTeXError> {
1473        if self.NumberOfTrains() == 0 {
1474            Err(CreateLaTeXError::NoTrainsError)
1475        } else {
1476            // Get formatting sizes (column widths).
1477            let StationColWidth = Self::getdouble(self.GetPrintOption("StationColWidth"),1.5);
1478            let TimeColWidth    = Self::getdouble(self.GetPrintOption("TimeColWidth"),0.5);
1479            // Figure out how many trains will fit across a page. 
1480            let maxTrains = ((7.0 - StationColWidth - TimeColWidth)/TimeColWidth) as usize;
1481            let UseMultipleTables: bool;        // Use multiple tables???
1482            let mut GroupBy: GroupMode = GroupMode::NoGrouping;
1483            // If there are more trains than will fit on a page, default to using
1484            // multiple tables, otherwise default to using a single table.
1485            if self.NumberOfTrains() >= maxTrains {
1486                UseMultipleTables = Self::getbool(self.GetPrintOption("UseMultipleTables"),true);
1487                self.table_of_contents_p = Self::getbool(self.GetPrintOption("TOCP"),true);
1488                GroupBy =  match self.GetPrintOption("GroupBy") {
1489                    None => GroupMode::Class,
1490                    Some(g) => match g.as_str() {
1491                                ""|"Class" => GroupMode::Class,
1492                                _ => GroupMode::NoGrouping,
1493                    },
1494                };
1495            } else {
1496                UseMultipleTables = Self::getbool(self.GetPrintOption("UseMultipleTables"),false);
1497                self.table_of_contents_p = Self::getbool(self.GetPrintOption("TOCP"),UseMultipleTables);
1498                if UseMultipleTables {
1499                    GroupBy =  match self.GetPrintOption("GroupBy") {
1500                        None => GroupMode::Class,
1501                        Some(g) => match g.as_str() {
1502                                    ""|"Class" => GroupMode::Class,
1503                                    _ => GroupMode::NoGrouping,
1504                        },
1505                    };
1506                }
1507            }
1508            // Get the logical direction name.
1509            self.direction_name = match self.GetPrintOption("DirectionName") {
1510                None => String::from("Northbound"),
1511                Some(s) => if s == "" {
1512                            String::from("Northbound")
1513                           } else {
1514                            s.to_string()
1515                           },
1516            };
1517            // Single or double sided formatting?
1518            let NSides = match self.GetPrintOption("NSides") {
1519                None => "single",
1520                Some(s) => if s == "" {
1521                            "single"
1522                        } else {
1523                            s
1524                        }
1525            };
1526            // Time format.
1527            let TimeFormat = match self.GetPrintOption("TimeFormat") {
1528                None => "24",
1529                Some(s) => if s == "" {
1530                            "24"
1531                        }  else {
1532                            s
1533                        }
1534            };
1535            let AMPMFormat = match self.GetPrintOption("AMPMFormat") {
1536                None => "a",
1537                Some(s) => if s == "" {
1538                            "a"
1539                           } else {
1540                            s
1541                           }
1542            };
1543            // Title, subtitle, and date of the time table. 
1544            let Title = match self.GetPrintOption("Title") {
1545                None => "My Model Railroad Timetable",
1546                Some(s) => if s == "" {
1547                            "My Model Railroad Timetable" 
1548                           } else {
1549                             s
1550                           }
1551            };
1552            let SubTitle = match self.GetPrintOption("SubTitle") {
1553                None => "Employee Timetable Number 1",
1554                Some(s) => if s == "" {
1555                            "Employee Timetable Number 1" 
1556                           } else {
1557                             s
1558                           }
1559            };
1560            let Date = match self.GetPrintOption("Date") {
1561                None => "\\today",
1562                Some(s) => if s == "" {
1563                            "\\today" 
1564                           } else {
1565                             s
1566                           }
1567            };
1568            // LaTeX code to include in the preamble. 
1569            let ExtraPreamble = match self.GetPrintOption("ExtraPreamble") {
1570                None => "",
1571                Some(s) => s,
1572            };
1573            // LaTeX code to put before the table of contents (eg on the title
1574            // page).  Typically this will be a cover (logo) graphic or other
1575            // such content.
1576            let BeforeTOC = match self.GetPrintOption("BeforeTOC") {
1577                None => "%\n% Insert Pre TOC material here.  Cover graphic, logo, etc.\n%",
1578                Some(s) => if s == "" {
1579                        "%\n% Insert Pre TOC material here.  Cover graphic, logo, etc.\n%"
1580                        } else {s},
1581            };
1582            // LaTeX code to put a the start of the Notes section.
1583            let NotesTOP = match self.GetPrintOption("NotesTOP") {
1584                None => "%\n% Insert notes prefix info here.\n%",
1585                Some(s) => if s == "" {
1586                        "%\n% Insert notes prefix info here.\n%"
1587                        } else {s},
1588            };
1589            
1590            // Get lists of trains. 
1591
1592            let mut allTrains: TrainList = Vec::new();
1593            let mut forwardTrains: TrainList = Vec::new();
1594            let mut backwardTrains: TrainList = Vec::new();
1595            // Loop through train map, collecting all trains, forward moving
1596            // trains (assending station indexes) and backward moving trains
1597            // (decending station indexes).
1598            for tr in self.trains.values() {
1599                allTrains.push(tr);
1600                let s1 = tr.StopI(0).unwrap();
1601                let s2 = tr.StopI(1).unwrap();
1602                if s1.StationIndex() < s2.StationIndex() {
1603                    forwardTrains.push(tr);
1604                } else {
1605                    backwardTrains.push(tr);
1606                }
1607            }
1608            let out = File::create(filename)?;
1609            let mut fp =  BufWriter::new(out);
1610            // Output LaTeX preamble. 
1611            writeln!(fp, "{}nonstopmode", Self::BACKSLASH)?;
1612            if NSides == "double" {
1613                writeln!(fp, "{}documentclass[notitlepage,twoside]{{article}}",
1614                            Self::BACKSLASH)?;
1615            } else {
1616                writeln!(fp, "{}documentclass[notitlepage]{{article}}",
1617                            Self::BACKSLASH)?;
1618            }
1619            writeln!(fp, "\n{}usepackage{{TimeTable}}", Self::BACKSLASH)?;
1620            writeln!(fp, "{}usepackage{{supertabular}}", Self::BACKSLASH)?;
1621            writeln!(fp, "{}usepackage{{graphicx}}", Self::BACKSLASH)?;
1622            if ExtraPreamble != "" {
1623                writeln!(fp, "{}", ExtraPreamble)?;
1624            }
1625            if !self.table_of_contents_p {
1626                writeln!(fp, "{}nofiles", Self::BACKSLASH)?;
1627            }
1628            if TimeFormat == "24" {
1629                writeln!(fp, "{}newcommand{{{}shtime}}{{{}rrtimetwentyfour}}",
1630                            Self::BACKSLASH,Self::BACKSLASH,Self::BACKSLASH)?;
1631            } else {
1632                writeln!(fp, "{}newcommand{{{}shtime}}{{{}rrtimetwelve{}}}",
1633                            Self::BACKSLASH,Self::BACKSLASH,Self::BACKSLASH,
1634                            AMPMFormat)?;
1635            }
1636            writeln!(fp, "")?;
1637            if StationColWidth != 1.5 {
1638                writeln!(fp, "{}setlength{{{}stationwidth}}{{{}in}}",
1639                        Self::BACKSLASH,Self::BACKSLASH,StationColWidth)?;
1640                writeln!(fp, "{}setlength{{{}stationwidthonear}}{{{}stationwidth}}",
1641                        Self::BACKSLASH,Self::BACKSLASH,Self::BACKSLASH)?;
1642                writeln!(fp, "{}advance{}stationwidthonear by -.25in",
1643                        Self::BACKSLASH,Self::BACKSLASH)?;
1644                writeln!(fp, "{}setlength{{{}stationwidthtwoar}}{{{}stationwidth}}",
1645                        Self::BACKSLASH,Self::BACKSLASH,Self::BACKSLASH)?;
1646                writeln!(fp, "{}advance{}stationwidthtwoar by -.25in",
1647                        Self::BACKSLASH,Self::BACKSLASH)?;
1648            }
1649            if TimeColWidth != 0.5 {
1650                writeln!(fp, "{}setlength{{{}timecolumnwidth}}{{{}in}}",
1651                        Self::BACKSLASH,Self::BACKSLASH,TimeColWidth)?;
1652            }
1653            writeln!(fp, "{}title{{{}}}", Self::BACKSLASH,Title)?;
1654            writeln!(fp, "{}author{{{}}}", Self::BACKSLASH,SubTitle)?;
1655            writeln!(fp, "{}date{{{}}}", Self::BACKSLASH,Date)?;
1656            writeln!(fp, "{}begin{{document}}",Self::BACKSLASH)?;
1657
1658            // Generate title.
1659            writeln!(fp, "{}maketitle",Self::BACKSLASH)?;
1660                
1661            // User supplied title page material.
1662            writeln!(fp, "{}", BeforeTOC)?;
1663
1664            if self.table_of_contents_p {
1665                 writeln!(fp, "{}tableofcontents",Self::BACKSLASH)?;
1666            }
1667
1668            // Branch off depending on how many tables and how the tables are
1669            // grouped. 
1670            if UseMultipleTables && GroupBy == GroupMode::Class {
1671                // Multiple tables, grouped by class.
1672                self.MakeTimeTableGroupByClass(&mut fp,&allTrains,
1673                                                &forwardTrains,
1674                                                &backwardTrains)?;
1675            } else if self.NumberOfTrains() > maxTrains {
1676                // Multiple tables, grouped manually.
1677                self.MakeTimeTableGroupManually(&mut fp,maxTrains,
1678                                                &mut allTrains,
1679                                                &mut forwardTrains,
1680                                                &mut backwardTrains)?;
1681            } else {
1682                // Single table for all trains. 
1683                let header = match self.GetPrintOption("AllTrainsHeader") {
1684                    None => "All Trains",
1685                    Some(s) => if s == "" {
1686                        "All Trains"
1687                    } else {s},
1688                };
1689                let sectionTOP = match self.GetPrintOption("AllTrainsSectionTOP") {
1690                    None => "",
1691                    Some(s) => s,
1692                };
1693                forwardTrains.sort_unstable_by(|a, b| a.DepartureCompare(b));
1694                backwardTrains.sort_unstable_by(|a, b| a.DepartureCompare(b)); 
1695                self.MakeTimeTableOneTable(&mut fp,&allTrains,&forwardTrains,
1696                                            &backwardTrains,header,
1697                                            sectionTOP)?;
1698            }
1699            // Generate notes section if there are any notes. 
1700            if self.NumberOfNotes() > 0 {
1701                // Fresh page.
1702                writeln!(fp,"{}clearpage",Self::BACKSLASH)?;
1703                // Section heading.
1704                writeln!(fp,"{}section*{{Notes}}",Self::BACKSLASH)?;
1705                if self.table_of_contents_p {
1706                    writeln!(fp,"{}addcontentsline{{toc}}{{section}}{{Notes}}",
1707                             Self::BACKSLASH)?;
1708                }
1709                // User supplied content. 
1710                writeln!(fp,"{}",NotesTOP)?;
1711                // Output notes as a LaTeX description environment. 
1712                writeln!(fp,"{}begin{{description}}",Self::BACKSLASH)?;
1713                for nt in 0..self.NumberOfNotes() {
1714                    let mut note = self.notes[nt].clone();
1715                    // Make sure we have proper punctuation (we don't want to be
1716                    // busted by the Grammar Police). 
1717                    if !note.ends_with(['.','?','!']) {
1718                        note += ".";
1719                    }
1720                    writeln!(fp,"{}item[{}] {}",Self::BACKSLASH,nt+1,note)?;
1721                }
1722                writeln!(fp,"{}end{{description}}",Self::BACKSLASH)?;
1723            }
1724            // End of document. 
1725            writeln!(fp, "{}end{{document}}", Self::BACKSLASH)?;
1726            Ok(())
1727        }
1728    }
1729    /**********************************************************************
1730     * Create a series of time tables, one for each class of train.       *
1731     * This private method loops over the set of train classes generating *
1732     * one table for each class of train.				  *
1733     **********************************************************************/
1734    fn MakeTimeTableGroupByClass(&self,fp: &mut BufWriter<File>,
1735                                 allTrains: &TrainList,
1736                                 forwardTrains: &TrainList,
1737                                 backwardTrains: &TrainList) 
1738                                            -> Result<(),CreateLaTeXError> {
1739        let mut classlist: Vec<u32> = Vec::new(); // (Sorted) list of classes. 
1740        // Loop over all trains, collecting unique class numbers.
1741        for tr in allTrains.iter() {
1742            let classnumber = tr.ClassNumber();
1743            if !classlist.contains(&classnumber) {
1744                classlist.push(classnumber);
1745            }
1746        }
1747        classlist.sort_unstable();
1748        // For each class, collect the trains for that class as three lists
1749        // (all, forward, backward).  Generate a table for each class.
1750        for classI in classlist.iter() {
1751            let mut fcl: TrainList = Vec::new();
1752            let mut bcl: TrainList = Vec::new();
1753            let mut acl: TrainList = Vec::new();
1754            for tr in forwardTrains.iter() {
1755                if tr.ClassNumber() == *classI {fcl.push(tr);}
1756            }
1757            for tr in backwardTrains.iter() {
1758                if tr.ClassNumber() == *classI {bcl.push(tr);}
1759            }
1760            for tr in allTrains.iter() {
1761                if tr.ClassNumber() == *classI {acl.push(tr);}
1762            }
1763            // Get or create group header
1764            let temp = format!("Group,{},ClassHeader",*classI);
1765            let classHeader = match self.GetPrintOption(temp.as_str()) {
1766                None => format!("Class {} trains",*classI),
1767                Some(s) => if s == "" {
1768                    format!("Class {} trains",*classI)
1769                } else {s.clone()},
1770            };
1771            // Get or create user content. 
1772            let temp = format!("Group,{},SectionTOP",*classI);
1773            let sectionTOP = match self.GetPrintOption(temp.as_str()) {
1774                None => "",
1775                Some(s) => s,
1776            };
1777            // Call helper method to generate the table.
1778            fcl.sort_unstable_by(|a, b| a.DepartureCompare(b));
1779            bcl.sort_unstable_by(|a, b| a.DepartureCompare(b)); 
1780            self.MakeTimeTableOneTable(fp,&acl,&fcl,&bcl,
1781                                       classHeader.as_str(),sectionTOP)?;
1782        }
1783        Ok(())
1784    }
1785    /**********************************************************************
1786     * Create a series of time tables, one for each manually selected     *
1787     * group of trains. This private method loops over the set of manually*
1788     * selected group of trains.                                          *
1789     **********************************************************************/
1790    fn MakeTimeTableGroupManually(&self,fp: &mut BufWriter<File>,
1791                                  maxTrains: usize,allTrains: &mut TrainList,
1792                                  forwardTrains: &mut TrainList,
1793                                  backwardTrains: &mut TrainList)
1794                                            -> Result<(),CreateLaTeXError> {
1795        // Loop over groups until all trains have been printed.
1796        let mut igroup: u32 = 1;
1797        loop {
1798            if allTrains.is_empty() {break;}
1799            // Get class header 
1800            let temp = format!("Group,{},ClassHeader",igroup);
1801            let classHeader = match self.GetPrintOption(temp.as_str()) {
1802                None => format!("Class {} trains",igroup),
1803                Some(s) => if s == "" {
1804                    format!("Class {} trains",igroup)
1805                } else {s.clone()},
1806            };
1807            // Get user content for this group. 
1808            let temp = format!("Group,{},SectionTOP",igroup);
1809            let sectionTOP = match self.GetPrintOption(temp.as_str()) { 
1810                None => "",
1811                Some(s) => s, 
1812            };
1813            // Get list of train numbers in this group. 
1814            let temp = format!("Group,{},Trains",igroup);
1815            let trainlist = match self.GetPrintOption(temp.as_str()) {
1816                None => "",
1817                Some(s) => s,
1818            };
1819            let listOfTrains = match StringListFromString(trainlist.to_string()) {
1820                None => {return Err(CreateLaTeXError::GroupSyntaxError(igroup));},
1821                Some(sl) => sl,
1822            };
1823            // If we have exhausted the groups but have not printed all trains
1824            // report a problem.
1825            if listOfTrains.len() == 0 && allTrains.len() > 0 {
1826                return Err(CreateLaTeXError::GroupEmpty(igroup));
1827            }
1828            // Collect trains for this group.
1829            let mut fcl: TrainList = Vec::new();
1830            let mut bcl: TrainList = Vec::new();
1831            let mut acl: TrainList = Vec::new();
1832            let mut fclI: Vec<usize> = Vec::new();
1833            let mut bclI: Vec<usize> = Vec::new();
1834            let mut aclI: Vec<usize> = Vec::new();
1835            let mut I: usize = 0;
1836            for tr in allTrains.iter() {
1837                if listOfTrains.contains(&tr.Number()) {
1838                    acl.push(tr);
1839                    aclI.push(I);
1840                }
1841                I += 1;
1842            }
1843            for indx in aclI.iter() {
1844                allTrains.remove(*indx);
1845            }
1846            I = 0;
1847            for tr in forwardTrains.iter() {
1848                if listOfTrains.contains(&tr.Number()) {
1849                    fcl.push(tr);
1850                    fclI.push(I);
1851                }
1852                I += 1;
1853            }
1854            for indx in fclI.iter() {
1855                forwardTrains.remove(*indx);
1856            }
1857            I = 0;
1858            for tr in backwardTrains.iter() {
1859                if listOfTrains.contains(&tr.Number()) {
1860                    bcl.push(tr); 
1861                    bclI.push(I); 
1862                }
1863                I += 1;
1864            }
1865            for indx in bclI.iter() {
1866                backwardTrains.remove(*indx);
1867            }
1868            // Print out this group of trains.
1869            fcl.sort_unstable_by(|a, b| a.DepartureCompare(b));
1870            bcl.sort_unstable_by(|a, b| a.DepartureCompare(b)); 
1871            self.MakeTimeTableOneTable(fp,&acl,&fcl,&bcl,&classHeader,sectionTOP)?;
1872            igroup += 1;
1873        }
1874        Ok(())
1875    }
1876    /**********************************************************************
1877     * Create one time table, given a list of trains.                     *
1878     * If there are only forward moving trains (typical of loop layouts)  *
1879     * generate a table with stations listed in the left column, otherwise*
1880     * generate a table with the stations in the center column.           *
1881     **********************************************************************/
1882    fn MakeTimeTableOneTable(&self,fp: &mut BufWriter<File>,
1883                             allTrains: &TrainList,forwardTrains: &TrainList,
1884                             backwardTrains: &TrainList,header: &str,
1885                             sectionTOP: &str) -> Result<(),CreateLaTeXError> {
1886        if backwardTrains.is_empty() {
1887            self.MakeTimeTableOneTableStationsLeft(fp,forwardTrains,header,sectionTOP)
1888        } else {
1889            self.MakeTimeTableOneTableStationsCenter(fp,forwardTrains,backwardTrains,header,sectionTOP)
1890        }
1891    }
1892    /**********************************************************************
1893     * Make a table with the stations listed on the left (all trains      *
1894     * in a single (logical) direction).                                  *
1895     **********************************************************************/
1896    fn MakeTimeTableOneTableStationsLeft(&self,fp: &mut BufWriter<File>,
1897                                        trains: &TrainList,
1898                                        header: &str,sectionTOP: &str) 
1899                                            -> Result<(),CreateLaTeXError> {
1900        let mut timesAtStations: TrainTimesAtStation = HashMap::new();
1901        self.ComputeTimes(trains,&mut timesAtStations);  // Compute time cells.
1902        let ntrains = trains.len();                 // Number of trains.
1903
1904        // Start on a fresh page. 
1905        writeln!(fp,"{}clearpage",Self::BACKSLASH)?;
1906        // Output section header. 
1907        writeln!(fp,"{}section*{{{}}}",Self::BACKSLASH,header)?;
1908        // Include TOC information.
1909        if self.table_of_contents_p {
1910            writeln!(fp,"{}addcontentsline{{toc}}{{section}}{{{}}}",
1911                    Self::BACKSLASH,header)?;
1912            for tr in trains.iter() {
1913                writeln!(fp,"{}addcontentsline{{toc}}{{subsection}}{{{}}}",
1914                    Self::BACKSLASH,tr.Number())?;
1915            }
1916        }
1917        // Output user content.
1918        writeln!(fp,"{}",sectionTOP)?;
1919        // The table will be generated as a supertabular environment.
1920        write!(fp,"{}begin{{supertabular}}{{|r|p{{{}stationwidth}}|",
1921                Self::BACKSLASH,Self::BACKSLASH)?;
1922        for itr in 0..ntrains {write!(fp,"r|")?;}
1923        writeln!(fp,"}}")?;
1924        writeln!(fp,"{}hline",Self::BACKSLASH)?;
1925	// Column headings.
1926        write!(fp,"&{}parbox{{{}timecolumnwidth}}{{Train number:{}{}name:{}{}class:}}",
1927               Self::BACKSLASH,Self::BACKSLASH,Self::BACKSLASH,
1928               Self::BACKSLASH,Self::BACKSLASH,Self::BACKSLASH)?;
1929        for tr in trains.iter() {
1930            let number = tr.Number();
1931            let name   = tr.Name();
1932            let classnumer = tr.ClassNumber();
1933            write!(fp,"&{}parbox{{{}timecolumnwidth}}{{{}{}{}{}{}{}{}}}",
1934                    Self::BACKSLASH,Self::BACKSLASH,number,
1935                    Self::BACKSLASH,Self::BACKSLASH,name,
1936                    Self::BACKSLASH,Self::BACKSLASH,classnumer)?;
1937        }
1938        writeln!(fp,"{}{}",Self::BACKSLASH,Self::BACKSLASH)?;
1939        writeln!(fp,"{}hline",Self::BACKSLASH)?;
1940        // Second line of column headings.
1941	write!(fp,"&Notes:")?;
1942        for tr in trains.iter() {
1943            write!(fp,"&{}parbox{{{}timecolumnwidth}}{{",Self::BACKSLASH,
1944                    Self::BACKSLASH)?;
1945            let numnotes = tr.NumberOfNotes();
1946            for inote in 0..numnotes {write!(fp,"{} ",
1947                                             tr.Note(inote).unwrap())?;}
1948            write!(fp,"}}")?;
1949        }
1950        writeln!(fp,"{}{}",Self::BACKSLASH,Self::BACKSLASH)?;
1951        writeln!(fp,"{}hline",Self::BACKSLASH)?;
1952        // Third line of column headings. 
1953        writeln!(fp,"Mile&Station&{}multicolumn{{{}}}{{|c|}}{{{}  (Read Down)}}{}{}",
1954                Self::BACKSLASH,ntrains,self.direction_name,Self::BACKSLASH,
1955                Self::BACKSLASH)?;
1956        writeln!(fp,"{}hline",Self::BACKSLASH)?;
1957        // Output 3 rows for each station (even ones where no trains stop).
1958        let numstations = self.NumberOfStations();
1959        for istation in 0..numstations {
1960            // Three rows per station:
1961            //    station name AR | train1 arrival/track | train2 arrival/track | ... trainN arrival/track |
1962            //    scale mile      | train1 cab+notes     | train2 cab+notes     | ... trainN cab+notes     |
1963            //                 LV | train1 depart/track  | train2 depart/track  | ... trainN depart/track  |
1964            // Station column.
1965            let tas = match timesAtStations.get(&istation) {
1966                None => {continue;},
1967                Some(t) => t,
1968            };
1969            let station = &self.stations[istation];
1970            let smile = station.SMile();
1971            // Train Arival time row
1972            // Station name and AR in station column.
1973            write!(fp,"&{}parbox[t]{{{}stationwidthonear}}{{{}}}{}hfill AR",
1974                    Self::BACKSLASH,Self::BACKSLASH,station.Name(),
1975                        Self::BACKSLASH)?;
1976            for tr in trains.iter() {
1977                write!(fp,"&")?;
1978                let st = match tas.get(&tr.Number()) {
1979                    None => {continue;},
1980                    Some(t) => t,
1981                };
1982                if st.Flag() != StopFlagType::Origin {
1983                    write!(fp,"{}shtime{{{}}}",Self::BACKSLASH,
1984                            (st.Arrival()+0.5) as u32)?;
1985                } else {
1986                    let origStop = tr.StopI(0).unwrap();
1987                    let tk = origStop.StorageTrackName();
1988                    if tk.len() > 0 {
1989                        write!(fp,"Tr {}",tk)?;
1990                    }
1991                }
1992            }
1993            writeln!(fp,"{}{}",Self::BACKSLASH,Self::BACKSLASH)?;
1994            // Train Cab and notes row 
1995            // Scale Mile in station column.
1996            write!(fp,"{}&",smile as u32)?;
1997            for tr in trains.iter() {
1998                write!(fp,"&")?;
1999                match tas.get(&tr.Number()) {
2000                    None => {continue;},
2001                    Some(t) => (),
2002                };
2003                let nstops = tr.NumberOfStops();
2004                for istop in 0..nstops {
2005                    let stop = tr.StopI(istop).unwrap();
2006                    if stop.StationIndex() == istation {
2007                        write!(fp,"{}parbox{{{}timecolumnwidth}}{{",
2008                                Self::BACKSLASH,Self::BACKSLASH)?;
2009                        match stop.TheCab() {
2010                            None => (),
2011                            Some(cab) => {
2012                                write!(fp,"{}{}{}",cab.Name(),Self::BACKSLASH,
2013                                        Self::BACKSLASH)?;
2014                            },
2015                        };
2016                        for inote in 0..stop.NumberOfNotes() {
2017                            write!(fp,"{} ",stop.Note(inote).unwrap())?;
2018                        }
2019                        write!(fp,"}}")?;
2020                        break;
2021                    }
2022                }
2023            }
2024            writeln!(fp,"{}{}",Self::BACKSLASH,Self::BACKSLASH)?;
2025            // Train departure times.
2026            // LV in station column.
2027            write!(fp,"&{}hfill LV",Self::BACKSLASH)?;
2028            for tr in trains.iter() {
2029                write!(fp,"&")?;
2030                let st = match tas.get(&tr.Number()) {
2031                    None => {continue;}
2032                    Some(st) => st,
2033                };
2034                if st.Flag() != StopFlagType::Terminate {
2035                    write!(fp,"{}shtime{{{}}}",Self::BACKSLASH,
2036                            (st.Departure()+0.5) as u32)?;
2037                } else {
2038                    let destStop = tr.StopI(tr.NumberOfStops()-1).unwrap();
2039                    let strack = destStop.StorageTrackName();
2040                    if strack.len() > 0 {
2041                        write!(fp,"Tr {}",strack)?;
2042                    }
2043                }
2044            }
2045            writeln!(fp,"{}{}",Self::BACKSLASH,Self::BACKSLASH)?;
2046            writeln!(fp,"{}hline",Self::BACKSLASH)?;
2047        }
2048        writeln!(fp,"{}end{{supertabular}}",Self::BACKSLASH)?;
2049        writeln!(fp,"")?;
2050        writeln!(fp,"{}vfill",Self::BACKSLASH)?;
2051        writeln!(fp,"")?;
2052        Ok(())
2053    }
2054    /**********************************************************************
2055     * Make a table with the stations listed in the center. Traffic is    *
2056     * bidirectional, with forward traveling trains on the left and       *
2057     * reverse traveling trains on the right.                             *
2058     **********************************************************************/
2059    fn MakeTimeTableOneTableStationsCenter(&self,fp: &mut BufWriter<File>,
2060                                           forwardTrains: &TrainList,
2061                                           backwardsTrains: &TrainList, 
2062                                           header: &str,sectionTOP: &str) 
2063                                            -> Result<(),CreateLaTeXError> {
2064
2065        // Reverse direction.
2066        let rev = match self.direction_name.as_str() {
2067            "Northbound" => "Southbound",
2068            "Southbound" => "Northbound",
2069            "Eastbound" => "Westbound",
2070            "Westbound" => "Eastbound",
2071            _ => "",
2072        };
2073        // Time cell matrixes 
2074        let mut timesAtStationsForward: TrainTimesAtStation = HashMap::new();
2075        let mut timesAtStationsBackward: TrainTimesAtStation = HashMap::new();
2076        self.ComputeTimes(forwardTrains,&mut timesAtStationsForward);
2077        self.ComputeTimes(backwardsTrains,&mut timesAtStationsBackward);
2078        // Numbers of trains.
2079        let nFtrains = forwardTrains.len();
2080        let nBtrains = backwardsTrains.len();
2081
2082        // Start on a fresh page.  
2083        writeln!(fp,"{}clearpage",Self::BACKSLASH)?;
2084        // Output section header.
2085        writeln!(fp,"{}section*{{{}}}",Self::BACKSLASH,header)?;
2086        // Include TOC information.
2087        if self.table_of_contents_p {
2088            writeln!(fp,"{}addcontentsline{{toc}}{{section}}{{{}}}",Self::BACKSLASH,header)?;
2089            for train in forwardTrains.iter() {
2090                writeln!(fp,"{}addcontentsline{{toc}}{{subsection}}{{{}}}",Self::BACKSLASH,train.Number())?;
2091            }
2092            for train in backwardsTrains.iter() {
2093                writeln!(fp,"{}addcontentsline{{toc}}{{subsection}}{{{}}}",Self::BACKSLASH,train.Number())?;
2094            }
2095        }
2096        // Output user content.
2097        writeln!(fp,"{}",sectionTOP)?;
2098        // The table will be generated as a supertabular environment.
2099        write!(fp,"\n{}begin{{supertabular}}{{|",Self::BACKSLASH)?;
2100        for itr in 0..nFtrains {write!(fp,"r|")?;}
2101        write!(fp,"|r|p{{{}stationwidth}}|",Self::BACKSLASH)?;
2102        for itr in 0..nBtrains {write!(fp,"r|")?;}
2103        writeln!(fp,"}}")?;
2104        writeln!(fp,"{}hline",Self::BACKSLASH)?;
2105        // Column headings.
2106        for train in forwardTrains.iter() {
2107            let number = train.Number();
2108            let name   = train.Name();
2109            let classnumer = train.ClassNumber();
2110            write!(fp,"{}parbox{{{}timecolumnwidth}}{{{}{}{}{}{}{}{}}}&",
2111                    Self::BACKSLASH,Self::BACKSLASH,number,
2112                    Self::BACKSLASH,Self::BACKSLASH,name,
2113                    Self::BACKSLASH,Self::BACKSLASH,classnumer)?;
2114        }
2115        write!(fp,"{}parbox{{{}timecolumnwidth}}{{Train number:{}{}name:{}{}class:}}&",
2116                Self::BACKSLASH,Self::BACKSLASH,
2117                Self::BACKSLASH,Self::BACKSLASH,
2118                Self::BACKSLASH,Self::BACKSLASH)?;
2119        for train in backwardsTrains.iter() {
2120            let number = train.Number();
2121            let name   = train.Name();
2122            let classnumer = train.ClassNumber();
2123            write!(fp,"&{}parbox{{{}timecolumnwidth}}{{{}{}{}{}{}{}{}}}",
2124                    Self::BACKSLASH,Self::BACKSLASH,number,
2125                    Self::BACKSLASH,Self::BACKSLASH,name,
2126                    Self::BACKSLASH,Self::BACKSLASH,classnumer)?;
2127        }
2128        writeln!(fp,"{}{}",Self::BACKSLASH,Self::BACKSLASH)?;
2129        writeln!(fp,"{}hline",Self::BACKSLASH)?;
2130        for train in forwardTrains.iter() {
2131            write!(fp,"{}parbox{{{}timecolumnwidth}}{{",
2132                    Self::BACKSLASH,Self::BACKSLASH)?;
2133            let numnotes = train.NumberOfNotes();
2134            for inote in 0..numnotes {
2135                write!(fp,"{} ",train.Note(inote).unwrap())?;
2136            }
2137            write!(fp,"}}&")?;
2138        }
2139        write!(fp,"Notes:&")?;
2140        for train in backwardsTrains.iter() {
2141            write!(fp,"&{}parbox{{{}timecolumnwidth}}{{",
2142                    Self::BACKSLASH,Self::BACKSLASH)?;
2143            let numnotes = train.NumberOfNotes();
2144            for inote in 0..numnotes {
2145                write!(fp,"{} ",train.Note(inote).unwrap())?;
2146            }
2147            write!(fp,"}}")?;
2148        }
2149        writeln!(fp,"{}{}",Self::BACKSLASH,Self::BACKSLASH)?;
2150        writeln!(fp,"{}hline",Self::BACKSLASH)?;
2151        // Third line of column headings.
2152        writeln!(fp,"{}multicolumn{{{}}}{{|c|}}{{{}  (Read Down)}}&Mile&Station&{}multicolumn{{{}}}{{|c|}}{{{} (Read up)}}{}{}",
2153                 Self::BACKSLASH,nFtrains,self.direction_name,
2154                 Self::BACKSLASH,nBtrains,rev,
2155                 Self::BACKSLASH,Self::BACKSLASH)?;
2156        writeln!(fp,"{}hline",Self::BACKSLASH)?;
2157        writeln!(fp,"{}hline",Self::BACKSLASH)?;
2158        let numstations = self.NumberOfStations();
2159        // Output 3 rows for each station (even ones where no trains stop). 
2160        for istation in 0..numstations {
2161            // Three rows per station:
2162            //    | train arrivals/tracks | AR station name LV | train departs/tracks  |
2163            //    | train cabs+notes      |    scale mile      | train cabs+notes      |
2164            //    | train departs/tracks  | LV              AR | train arrivals/tracks |
2165            let tasF = timesAtStationsForward.get(&istation);
2166            let tasB = timesAtStationsBackward.get(&istation);
2167            if tasF.is_none() && tasB.is_none() {continue;}
2168            let station = self.IthStation(istation).unwrap();
2169            let smile = station.SMile();
2170            for train in forwardTrains.iter() {
2171                match tasF.unwrap().get(&train.Number()) {
2172                    Some(st) => {
2173                        if st.Flag() != StopFlagType::Origin {
2174                            write!(fp,"{}shtime{{{}}}",Self::BACKSLASH,
2175                                    (st.Arrival()+0.5) as u32)?;
2176                        } else {
2177                            let origStop = train.StopI(0).unwrap();
2178                            let strack = origStop.StorageTrackName();
2179                            if strack.len() > 0 {write!(fp,"Tr {}",strack)?;}
2180                        }
2181                    },
2182                    None => (),
2183                };
2184                write!(fp,"&")?;
2185            }
2186            // Center (station name).
2187            write!(fp,"&AR{}hfill{}parbox[t]{{{}stationwidthtwoar}}{{{}}}{}hfill LV",
2188                      Self::BACKSLASH,Self::BACKSLASH,Self::BACKSLASH,
2189                      station.Name(),Self::BACKSLASH)?;
2190            // Right side (backward trains).
2191            for train in backwardsTrains.iter() {
2192                match tasB.unwrap().get(&train.Number()) {
2193                    Some(st) => {
2194                        if st.Flag() != StopFlagType::Terminate {
2195                            write!(fp,"&{}shtime{{{}}}",Self::BACKSLASH,
2196                                    (st.Departure()+0.5) as u32)?;
2197                        } else {
2198                            write!(fp,"&")?;
2199                            let destStop = train.StopI(train.NumberOfStops()-1).unwrap();
2200                            let strack = destStop.StorageTrackName();
2201                            if strack.len() > 0 {write!(fp,"Tr {}",strack)?;}
2202                        }
2203                    },
2204                    None => {write!(fp,"&")?;},
2205                };
2206                
2207            }
2208            writeln!(fp,"{}{}",Self::BACKSLASH,Self::BACKSLASH)?;
2209            // Second row: cabs and notes + scale miles.
2210            for train in forwardTrains.iter() {
2211                match tasF.unwrap().get(&train.Number()) {
2212                    Some(st) => {
2213                        let nstops = train.NumberOfStops();
2214                        for istop in 0..nstops {
2215                            let stop = train.StopI(istop).unwrap();
2216                            if stop.StationIndex() == istation {
2217                                write!(fp,"{}parbox{{{}timecolumnwidth}}{{",
2218                                        Self::BACKSLASH,Self::BACKSLASH)?;
2219                                match stop.TheCab() {
2220                                    None => (),
2221                                    Some(cab) => {
2222                                        write!(fp,"{}{}{}",cab.Name(),
2223                                                Self::BACKSLASH,
2224                                                Self::BACKSLASH)?;
2225                                    },
2226                                };
2227                                let nnotes = stop.NumberOfNotes();
2228                                for inote in 0..nnotes {
2229                                    write!(fp,"{} ",
2230                                            stop.Note(inote).unwrap())?;
2231                                }
2232                                write!(fp,"}}")?;
2233                            }
2234                        }
2235                    },
2236                    None => (),
2237                };
2238                write!(fp,"&")?;
2239            }
2240            write!(fp,"{}&",(smile+0.5) as u32)?;
2241            for train in backwardsTrains.iter() {
2242                write!(fp,"&")?;
2243                match tasB.unwrap().get(&train.Number()) {
2244                    Some(st) => {
2245                        let nstops = train.NumberOfStops();
2246                        for istop in 0..nstops {
2247                            let stop = train.StopI(istop).unwrap();
2248                            if stop.StationIndex() == istation {
2249                                write!(fp,"{}parbox{{{}timecolumnwidth}}{{",
2250                                        Self::BACKSLASH,Self::BACKSLASH)?;
2251                                match stop.TheCab() {
2252                                    None => (),
2253                                    Some(cab) => {
2254                                        write!(fp,"{}{}{}",cab.Name(),
2255                                                Self::BACKSLASH,
2256                                                Self::BACKSLASH)?;
2257                                    },
2258                                };
2259                                let nnotes = stop.NumberOfNotes();
2260                                for inote in 0..nnotes {
2261                                    write!(fp,"{} ",
2262                                            stop.Note(inote).unwrap())?;
2263                                }
2264                                write!(fp,"}}")?;
2265                            }
2266                        }
2267                    },
2268                    None => (),
2269                };
2270            }
2271            writeln!(fp,"{}{}",Self::BACKSLASH,Self::BACKSLASH)?;
2272            // Third row: departures / tracks. 
2273            for train in forwardTrains.iter() {
2274                match tasF.unwrap().get(&train.Number()) {
2275                    Some(st) => {
2276                        if st.Flag() != StopFlagType::Terminate {
2277                            write!(fp,"{}shtime{{{}}}",Self::BACKSLASH,
2278                                    (st.Departure()+0.5) as u32)?;
2279                        } else {
2280                            let destStop = train.StopI(train.NumberOfStops()-1).unwrap();
2281                            let strack = destStop.StorageTrackName();
2282                            if strack.len() > 0 {write!(fp,"Tr {}",strack)?;}
2283                        }
2284                    },
2285                    None => (),
2286                };
2287                write!(fp,"&")?;
2288            }
2289            write!(fp,"&LV{}hfill AR",Self::BACKSLASH)?;
2290            for train in backwardsTrains.iter() {
2291                write!(fp,"&")?;
2292                match tasB.unwrap().get(&train.Number()) {
2293                    Some(st) => {
2294                        if st.Flag() != StopFlagType::Origin {
2295                            write!(fp,"{}shtime{{{}}}",Self::BACKSLASH,
2296                                    (st.Arrival()+0.5) as u32)?;
2297                        } else {
2298                            let origStop = train.StopI(0).unwrap();
2299                            let strack = origStop.StorageTrackName();
2300                            if strack.len() > 0 {write!(fp,"Tr {}",strack)?;}
2301                        }
2302                    }
2303                    None => (),
2304                };
2305            }
2306            writeln!(fp,"{}{}",Self::BACKSLASH,Self::BACKSLASH)?;
2307            writeln!(fp,"{}hline",Self::BACKSLASH)?;
2308        }
2309        writeln!(fp,"{}end{{supertabular}}",Self::BACKSLASH)?;
2310        writeln!(fp,"")?;
2311        writeln!(fp,"{}vfill",Self::BACKSLASH)?;
2312        writeln!(fp,"")?;
2313        Ok(())
2314    }
2315    /**********************************************************************
2316     * Helper method to compute station times.                            *
2317     * Iterates over trains and then iterates over the stations the train *
2318     * passes or stops at.  For each stop of each train, fill in a cell in*
2319     * the TrainTimesAtStation matrix, with the arrival and departure     *
2320     * times.                                                             *
2321     **********************************************************************/
2322    fn ComputeTimes(&self,trains: &TrainList,
2323                    timesAtStations: &mut TrainTimesAtStation) {
2324        // Loop over trains...
2325        for train in trains.iter() {
2326            let departure = train.Departure();
2327            let speed = train.Speed(); 
2328            let mut oldDepart: f64 = -1.0;
2329            let mut oldSmile:  f64 = -1.0; 
2330            let nstops = train.NumberOfStops();
2331            // Loop over stops...
2332            for i in 0..nstops {
2333                let stop = train.StopI(i).unwrap();
2334                let istop = stop.StationIndex();
2335                let station = self.IthStation(istop).unwrap();
2336                let smile = station.SMile();
2337                // compute arrival and departure times.
2338                let arrival: f64;
2339                if oldDepart >= 0.0 {
2340                    // Travel time at speed from previous station.
2341                    arrival = oldDepart + ((smile - oldSmile).abs() * 
2342                                (speed as f64/ 60.0));
2343                } else {
2344                    // Originating departure.
2345                    arrival = departure as f64;
2346                }
2347                let depart = stop.Departure(arrival);
2348                let st: StationTimes = StationTimes::new(arrival,depart,stop.Flag());
2349                let stationTimes = timesAtStations.entry(istop)
2350                                        .or_insert(HashMap::new());
2351                stationTimes.insert(train.Number(),st);
2352                oldDepart = depart;
2353                oldSmile  = smile;
2354            }
2355        }
2356    }
2357    fn getdouble(optstr: Option<&String>,default: f64) -> f64 {
2358        match optstr {
2359            None => default,
2360            Some(string) => string.parse::<f64>().unwrap_or(default),
2361        }
2362    }
2363    fn getbool(optstr: Option<&String>,default: bool) -> bool {
2364        match optstr { 
2365            None => default, 
2366            Some(string) => string.parse::<bool>().unwrap_or(default),
2367        }
2368    }
2369    /* ToDo: interators, private methods */
2370    fn ReadNote(inp: &mut BufReader<File>) -> std::io::Result<Option<String>> {
2371        let mut buffer: [u8; 1] = [0; 1];
2372        loop {
2373            let status = inp.read(&mut buffer)?;
2374            if status == 0 {return Ok(None);}
2375            let ch = buffer[0] as char;
2376            if ch == '"' {break;}
2377        }
2378        let mut EOF: bool = false;
2379        let mut result: String = String::new();
2380        loop {
2381            let mut status = inp.read(&mut buffer)?;
2382            if status == 0 {EOF = true;break;}
2383            let mut ch = buffer[0] as char;
2384            if ch == '"' {break;}
2385            if ch == '\\' {
2386                status = inp.read(&mut buffer)?;
2387                if status == 0 {EOF = true;break;}
2388                ch = buffer[0] as char;
2389            }
2390            result += &ch.to_string();
2391        }
2392        if EOF {
2393            if result.len() > 0 {
2394                Ok(Some(result))
2395            } else {
2396                Ok(None)
2397            }
2398        } else {
2399            Ok(Some(result))
2400        }       
2401    }
2402    fn WriteNote(f: &mut BufWriter<File>, string: &String) -> std::io::Result<()> {
2403        write!(f,"{}",'"')?;
2404        for ch in string.chars() {
2405            if ch == '"' || ch == '\\' {
2406                write!(f,"{}",'\\')?;
2407            }
2408            write!(f,"{}",ch)?;
2409        }
2410        write!(f,"{}",'"')
2411    }
2412
2413}
2414
2415
2416
2417
2418
2419
2420
2421
2422#[cfg(test)]
2423mod tests { 
2424    use super::*;
2425
2426    #[test]
2427    fn StationTimes_new () {
2428        let temp = StationTimes::new(4.2, 5.1, StopFlagType::Origin);
2429        assert_eq!(temp,StationTimes{arrival: 4.2, departure: 5.1, flag: StopFlagType::Origin });
2430    }
2431    #[test]
2432    fn StationTimes_Arrival () {
2433        let temp = StationTimes::new(4.2, 5.1, StopFlagType::Origin);
2434        assert_eq!(temp.Arrival(),4.2);
2435    }
2436    #[test]
2437    fn StationTimes_Departure () {
2438        let temp = StationTimes::new(4.2, 5.1, StopFlagType::Origin);
2439        assert_eq!(temp.Departure(),5.1);
2440    }
2441    #[test]
2442    fn StationTimes_Flag () {
2443        let temp = StationTimes::new(4.2, 5.1, StopFlagType::Origin);
2444        assert_eq!(temp.Flag(),StopFlagType::Origin);
2445    }
2446
2447    #[test]
2448    fn pub_StringListToString () {
2449        let mut temp: StringList = LinkedList::new();
2450        temp.push_back(String::from("Hello"));
2451        temp.push_back(String::from("World"));
2452        let output = StringListToString(temp);
2453        assert_eq!(output,String::from("\"Hello\",\"World\""));
2454    }
2455    #[test]
2456    fn pub_StringListFromString () {
2457        let temp = match StringListFromString(String::from("\"Hello\",\"World\"")) {
2458            None => panic!("Conversion failed"),
2459            Some(l) => l,
2460        };
2461        assert_eq!(temp.len(),2);
2462        assert_eq!(temp.front(),Some(&String::from("Hello")));
2463        assert_eq!(temp.back(),Some(&String::from("World")));
2464    }
2465
2466    //#[test]
2467    //fn TimeTableSystem_old () {
2468    //    let temp = TimeTableSystem::old("examples/LJandBS.tt")
2469    //                .expect("Failed");
2470    //}
2471    //#[test]
2472    //fn TimeTableSystem_CreateLaTeXTimetable () {
2473    //    let mut temp = TimeTableSystem::old("examples/LJandBS.tt")
2474    //                .expect("Failed");
2475    //    temp.CreateLaTeXTimetable("examples/LJandBS.tex").expect("Failed");
2476    //}
2477    #[test]
2478    fn TimeTableSystem_new () {
2479        let temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2480        assert_eq!(temp,TimeTableSystem {name: String::from("Test table"),
2481                                        filepath: PathBuf::new(), 
2482                                        timescale: 1440, timeinterval: 15,
2483                                        stations: Vec::new(), 
2484                                        cabs: HashMap::new(), 
2485                                        trains: HashMap::new(), 
2486                                        notes: Vec::new(), 
2487                                        print_options: HashMap::new(), 
2488                                        table_of_contents_p: true, 
2489                                        direction_name: String::new() })
2490    }
2491    #[test]
2492    fn TimeTableSystem_AddStation () {
2493        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2494        assert_eq!(temp.AddStation(String::from("CWC Station"),0.0),Some(0));
2495    }
2496    #[test]
2497    fn TimeTableSystem_FindStationByName () {
2498        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2499        temp.AddStation(String::from("CWC Station"),0.0);
2500        assert_eq!(temp.FindStationByName(String::from("CWC Station")),Some(0));
2501    }
2502    #[test]
2503    fn TimeTableSystem_NumberOfStations () {
2504        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2505        temp.AddStation(String::from("CWC Station"),0.0);
2506        assert_eq!(temp.NumberOfStations(),1);
2507    }
2508    #[test] 
2509    fn TimeTableSystem_IthStation () {
2510        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2511        temp.AddStation(String::from("CWC Station"),0.0);
2512        assert_eq!(temp.IthStation(0),Some(&Station::new(String::from("CWC Station"),0.0)));
2513    }
2514    #[test] 
2515    fn TimeTableSystem_StationName () {
2516        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2517        temp.AddStation(String::from("CWC Station"),0.0);
2518        assert_eq!(temp.StationName(0),Some(String::from("CWC Station")));
2519    }
2520    #[test] 
2521    fn TimeTableSystem_SMile () {
2522        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2523        temp.AddStation(String::from("CWC Station"),0.0);
2524        assert_eq!(temp.SMile(0),Some(0.0));
2525    }
2526    #[test]
2527    fn TimeTableSystem_TotalLength () {
2528        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2529        temp.AddStation(String::from("CWC Station"),0.0);
2530        temp.AddStation(String::from("Bench Station"),5.0);
2531        assert_eq!(temp.TotalLength(),5.0);
2532    }
2533    #[test]
2534    fn TimeTableSystem_DuplicateStationIndex () {
2535        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2536        temp.AddStation(String::from("CWC Station"),0.0);
2537        assert_eq!(temp.DuplicateStationIndex(0),None);
2538    }
2539    #[test]
2540    fn TimeTableSystem_SetDuplicateStationIndex () {
2541        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2542        temp.AddStation(String::from("CWC Station West"),0.0);
2543        temp.AddStation(String::from("Bench Station"),5.0);
2544        temp.AddStation(String::from("CWC Station East"),10.0);
2545        temp.SetDuplicateStationIndex(0,2);
2546        temp.SetDuplicateStationIndex(2,0);
2547        assert_eq!(temp.DuplicateStationIndex(0),Some(2));
2548        assert_eq!(temp.DuplicateStationIndex(2),Some(0));
2549    }
2550    #[test]
2551    fn TimeTableSystem_AddStorageTrack () {
2552        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2553        temp.AddStation(String::from("CWC Station"),0.0);
2554        assert_eq!(temp.AddStorageTrack(0,&String::from("Track 1")),
2555                    Some(&mut StorageTrack::new(String::from("Track 1"))));
2556    }
2557    #[test] 
2558    fn TimeTableSystem_FindStorageTrack () {
2559        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2560        temp.AddStation(String::from("CWC Station"),0.0);
2561        temp.AddStorageTrack(0,&String::from("Track 1"));
2562        assert_eq!(temp.FindStorageTrack(0,&String::from("Track 1")),
2563                    Some(&StorageTrack::new(String::from("Track 1"))));
2564    }
2565    #[test] 
2566    fn TimeTableSystem_AddCab () {
2567        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2568        assert_eq!(temp.AddCab(String::from("Blue"),String::from("blue")),
2569                    &Cab::new(String::from("Blue"),String::from("blue")));
2570    }
2571    #[test] 
2572    fn TimeTableSystem_FindCab () {
2573        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2574        temp.AddCab(String::from("Blue"),String::from("blue"));
2575        assert_eq!(temp.FindCab(&String::from("Blue")),
2576                    Some(&Cab::new(String::from("Blue"),String::from("blue"))));
2577    }
2578    #[test] 
2579    fn TimeTableSystem_NumberOfCabs () {
2580        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2581        temp.AddCab(String::from("Blue"),String::from("blue"));
2582        assert_eq!(temp.NumberOfCabs(),1);
2583    }
2584    #[test]
2585    fn TimeTableSystem_AddTrain () {
2586        let mut temp = TimeTableSystem::new(String::from("Test table"),1440,15);
2587        temp.AddStation(String::from("CWC Station West"),0.0);
2588        temp.AddStation(String::from("Bench Station"),5.0);
2589        temp.AddStation(String::from("CWC Station East"),10.0);
2590        assert_eq!(temp.AddTrain(String::from("Test Train"),String::from("T1"),
2591                                60,1,6*60,0,2),
2592                    Ok(&Train::new(String::from("Test Train"),
2593                                    String::from("T1"),60,1,6*60,0.0,0,2)));
2594    }
2595}