extern crate rand;
use std::cmp::Ordering;
use std::collections::HashMap;
use rand::{thread_rng, sample};
type Predecessor = (Option<String>, char, Option<String>);
type Fun = Box<Fn(&Vec<String>) -> String>;
pub struct LSystem {
axiom: String,
state: String,
rules: HashMap<Predecessor, Vec<String>>,
distributions: HashMap<Predecessor, Fun>,
}
impl LSystem {
pub fn with_axiom(axiom: &str) -> LSystem {
LSystem {
axiom: axiom.into(),
state: "".into(),
rules: HashMap::new(),
distributions: HashMap::new(),
}
}
pub fn add_rule(&mut self, predecessor: char, successor: &str) {
self.add_context_rule::<String>((None, predecessor, None), successor);
}
pub fn add_context_rule<T>(&mut self, predecessor: (Option<T>, char, Option<T>), successor: &str) where T: Into<String> {
let path = predecessor.0.map(|p| p.into());
let tree = predecessor.2.map(|t| t.into());
self.rules.entry((path, predecessor.1, tree)).or_insert(Vec::new()).push(successor.into());
}
pub fn add_distribution(&mut self, predecessor: char, distribution: Fun) {
self.distributions.insert((None, predecessor, None), distribution);
}
pub fn add_context_distribution(&mut self, predecessor: Predecessor, distribution: Fun) {
self.distributions.insert(predecessor, distribution);
}
pub fn reset(&mut self) {
self.state = "".into();
}
fn default(vals: &Vec<String>) -> String {
let mut rng = thread_rng();
sample(&mut rng, vals.into_iter(), 1)[0].clone()
}
fn get_sorted_rules(&self) -> Vec<(&Predecessor, &Vec<String>)> {
let mut rules: Vec<_> = self.rules.iter().collect();
rules.sort_by(|a, b| {
let key_1 = a.0;
let key_2 = b.0;
match (key_1, key_2) {
(&(Some(_), _, Some(_)), &(None, _, None)) => Ordering::Less,
(&(Some(_), _, None ), &(None, _, None)) => Ordering::Less,
(&(None, _, Some(_)), &(None, _, None)) => Ordering::Less,
(&(None, _, None), &(Some(_), _, Some(_))) => Ordering::Greater,
(&(None, _, None), &(Some(_), _, None )) => Ordering::Greater,
(&(None, _, None), &(None, _, Some(_))) => Ordering::Greater,
_ => key_1.cmp(key_2),
}
});
rules
}
fn get_successor(&self, ch: char, i: usize) -> Option<String> {
for (key, value) in self.get_sorted_rules() {
if key.1 != ch {
continue;
}
let (before, after) = self.state.split_at(i);
let before_matches = match key.0 {
Some(ref k) => {
let b = before
.to_string()
.chars()
.into_iter()
.rev()
.take(k.len())
.collect::<String>();
b == *k
}
None => true,
};
let after_matches = match key.2 {
Some(ref k) => {
let a = after
.to_string()
.chars()
.into_iter()
.take(k.len())
.collect::<String>();
a == *k
}
None => true,
};
if before_matches && after_matches {
return Some(match self.distributions.get(&key) {
Some(f) => f(&value),
None => LSystem::default(&value),
});
}
}
None
}
}
impl Iterator for LSystem {
type Item = String;
fn next(&mut self) -> Option<String> {
let mut next = String::new();
if self.state == "" {
self.state = self.axiom.clone();
return Some(self.state.clone());
}
for (i, ch) in self.state.chars().enumerate() {
match self.get_successor(ch, i) {
Some(successor) => {
next.push_str(&successor);
continue;
}
None => next.push(ch),
};
}
self.state = next.clone();
Some(next)
}
}