1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
use molecule::{ Molecule };
use reaction::{ ReactionCompound };
use trait_element::{ Element };
use trait_properties::{ Properties };
use trait_reaction::{ Reaction };
use types::*;

use std::hash::{ Hash, Hasher };


#[derive(Debug, Clone)]
/// A container for elements
pub struct Container<E: Element> {
    /// A vector with the contents of this container
    pub contents: Vec< ContainerCompound<E> >,


    /// The amount of energy available
    pub available_energy: Energy
}


#[derive(Debug, Clone)]
/// A compound for containers
pub struct ContainerCompound<E: Element> {
    /// The element it contains
    pub element: E,


    /// The amount of moles of this element
    pub moles: Moles
}


/// Convert a given ReactionCompound into a ContainerCompound
pub fn rc_to_cc<E: Element>(rc: ReactionCompound<E>) -> ContainerCompound<E> {
    ContainerCompound {
        element: rc.element,
        moles: rc.amount as Moles
    }
}


impl<E: Element> Container<E> {
    /// Applies given reaction to container
    /// Removing the elements on the left-hand side
    /// and adding the elements on the right-hand side.
    /// If there is enough energy for the reaction, that amount will be consumed
    /// otherwise the reaction won't occur.
    /// Returns if the reaction succeeded
    pub fn react<R: Reaction<E>>(&mut self, reaction: &R) -> bool {
        // Get required items
        let required_energy = reaction.energy_cost();
        let mut required_elements = vec! {};
        let mut resulting_elements = vec! {};

        // Convert lhs.compounds into ContainerCompound's
        for rc in reaction.elem_reaction().lhs.compounds.iter() {
            let cc = rc_to_cc(rc.clone());

            required_elements.push(cc);
        }

        // Convert rhs.compounds into ContainerCompound's
        for rc in reaction.elem_reaction().rhs.compounds.iter() {
            let cc = rc_to_cc(rc.clone());

            resulting_elements.push(cc);
        }

        // Check if the container has enough energy
        if self.available_energy < required_energy {
            println!("####    Not enough energy");
            return false;
        }

        // Check if the container has the required elements
        if ! self.has_elements(&required_elements) {
            println!("####    Not enough elements");
            return false;
        }

        // Subtract needed energy (or add, in case of an exothermic reaction)
        self.available_energy -= required_energy;

        // Remove required elements
        self.remove_elements(&required_elements);

        // Add reaction results
        self.add_elements(&resulting_elements);

        return true;
    }


    /// Check if the container has all given elements
    pub fn has_elements(&mut self, elements: &Vec< ContainerCompound<E> >) -> bool {
        'outer: for element in elements {
            // Find element in self.contents
            if let Some(position) = self.contents.iter().position(|comp| comp == element) {
                let compound = self.contents.get(position).unwrap();

                // Check if more elements are required than available
                if element.moles > compound.moles {
                    return false;
                }

                continue 'outer;
            }

            // Element is not available
            return false;
        }

        return true;
    }


    /// Remove given elements from container
    pub fn remove_elements(&mut self, elements: &Vec< ContainerCompound<E> >) {
        for element in elements {
            // Find element in self.contents
            if let Some(position) = self.contents.iter().position(|comp| comp == element) {
                if {
                    let mut compound = self.contents.get_mut(position).unwrap();

                    // Check if we have enough
                    if compound.moles < element.moles {
                        panic!("Can't remove element {} (not enough)", element.symbol());
                    }

                    // Remove amount
                    compound.moles -= element.moles;

                    // If none is available anymore, remove element from container in its entirety
                    compound.moles == 0.0
                } {
                    self.contents.remove(position);
                }
            } else {
                println!("Can't remove element {} (not found)", element.symbol());
            }
        }
    }


    /// Add given elements to container
    pub fn add_elements(&mut self, elements: &Vec< ContainerCompound<E> >) {
        for element in elements.into_iter() {
            // Find element in self.contents
            if let Some(position) = self.contents.iter().position(|comp| comp == element) {
                let mut compound = self.contents.get_mut(position).unwrap();

                // Add amount
                compound.moles += element.moles;
            } else {
                // If the element is not found in the container, add it

                self.contents.push(element.clone());
            }
        }
    }


    /// Convert container to a nice string for displaying
    pub fn stringify(&self) -> String {
        let mut string = String::new();

        let mut first = true;
        for compound in self.contents.iter() {
            if compound.moles > 0.0 {
                if ! first {
                    string += " + ";
                }
                first = false;

                string += &compound.stringify();
            }
        }

        string += &" [";
        string += &self.available_energy.to_string();
        string += &" J]";

        return string;
    }
}


impl<E: Element> Eq for ContainerCompound<E> {

}


impl<E: Element> PartialEq for ContainerCompound<E> {
    /// Two container compounds are equal when their elements are equal
    fn eq(&self, rhs: &ContainerCompound<E>) -> bool {
        self.element == rhs.element
    }
}


impl<E: Element> Hash for ContainerCompound<E> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.element.hash(state);
    }
}


impl<E: Element> Element for ContainerCompound<E> {
    fn get_charge(&self) -> Option<IonCharge> {
        self.element.get_charge()
    }


    fn get_molecule(&self) -> Option<&Molecule> {
        self.element.get_molecule()
    }
}


impl<E: Element> Properties for ContainerCompound<E> {
    fn symbol(&self) -> String {
        let mut symbol = String::new();
        symbol += &self.moles.to_string();
        symbol += &self.element.symbol();
        return symbol;
    }


    fn name(&self) -> String {
        let mut name = String::new();
        name += &self.moles.to_string();
        name += &self.element.name();
        return name;
    }


    fn mass(&self) -> AtomMass {
        self.element.mass()
    }
}