<commit_before>use itertools::Itertools;
use serde_json;
use std;
use std::collections::btree_map::BTreeMap;
use std::collections::btree_set::BTreeSet;

#[cfg(test)]
mod tests;

#[derive(Clone, Copy, Debug)]
pub enum RenderType {
    Full,
    Pedigree,
}

/// Represent the origin of a particular datum
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Origin {
    /// What Actor does this Origin correspond to
    pub source_id: String,
    /// What datums did the source use to construct the entity that this Origin corresponds to
    pub args: Vec<String>,
}

impl std::fmt::Display for Origin {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", serde_json::to_string(&self).unwrap())
    }
}

impl Origin {
    pub fn new() -> Self {
        Origin {
            source_id: String::new(),
            args: Vec::new(),
        }
    }

    pub fn with_source(source: String) -> Self {
        Origin {
            source_id: source,
            args: Vec::new(),
        }
    }

    pub fn ancestors(&self) -> Vec<&String> {
        self.args.iter().chain(std::iter::once(&self.source_id)).collect()
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InferenceGraphBackwardIterator<'a> {
    inf_graph: &'a InferenceGraph,
    next_generation: Option<usize>,
}

impl<'a> Iterator for InferenceGraphBackwardIterator<'a> {
    type Item = (Vec<(&'a String, Option<&'a Origin>)>, Vec<&'a BTreeSet<String>>);

    fn next(&mut self) -> Option<(Vec<(&'a String, Option<&'a Origin>)>, Vec<&'a BTreeSet<String>>)> {
        let current_generation = self.next_generation;

        self.next_generation = self.next_generation.and_then(|idx| if idx == 0 { None } else { Some(idx - 1) });

        let construct_id_origin_tuple = |current_id| {
            (current_id,
             self.inf_graph
                 .pedigree
                 .get_ancestor(current_id))
        };

        let construct_id_origin_tuples_for_generation = |generation: &'a BTreeSet<String>| {
            generation.iter()
                .map(construct_id_origin_tuple)
                .collect()
        };

        current_generation.and_then(|generation_idx| {
            self.inf_graph.entries_by_generation.get(generation_idx).and_then(|generation| {
                Some((construct_id_origin_tuples_for_generation(generation), self.inf_graph.subsequent_inferences((generation_idx + 1))))
            })
        })
    }
}

/// Provide a convenient interface to a particular inference graph.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InferenceGraph {
    pedigree: Pedigree,
    /// Track which generation each datum was created in; the root ocurrs in the last generation
    entries_by_generation: Vec<BTreeSet<String>>,
    /// Inverse of entries_by_generation; map each id into the generation it was introduced in
    entries_to_generation: BTreeMap<String, usize>,
    /// The endpoint data for the inference graph
    /// For forward chaining, this is the initial data used in the inference.
    /// For backward chaining, this is the final grounded results.
    leaves: BTreeSet<String>,
    /// The goal of the infernece
    root: String,
}

impl<'a> InferenceGraph {
    pub fn new(root: String) -> Self {
        InferenceGraph {
            pedigree: Pedigree::new(),
            entries_by_generation: Vec::new(),
            entries_to_generation: BTreeMap::new(),
            leaves: BTreeSet::new(),
            root: root,
        }
    }

    pub fn back_iter(&self) -> InferenceGraphBackwardIterator {
        InferenceGraphBackwardIterator {
            inf_graph: self,
            next_generation: Some(self.entries_by_generation.len() - 1),
        }
    }

    pub fn root(&self) -> &String {
        &self.root
    }

    pub fn leaves(&'a self) -> &'a BTreeSet<String> {
        &self.leaves
    }

    pub fn ancestor(&self, id: &String) -> Option<&Origin> {
        self.pedigree.get_ancestor(id)
    }

    pub fn descendent_inferences(&'a self, id: &String) -> Option<&'a BTreeSet<String>> {
        /// Return datums derived from this one
        self.pedigree.get_descendents(id)
    }

    pub fn subsequent_inferences(&'a self, generation: usize) -> Vec<&'a BTreeSet<String>> {
        /// Return all datums derived in and after the specified generation
        let mut subsequent_inferences = Vec::new();

        for entries in self.entries_by_generation
            .iter()
            .skip(generation) {
            subsequent_inferences.push(entries);
        }

        subsequent_inferences
    }

    pub fn all_ids(&'a self) -> BTreeSet<&'a String> {
        self.entries_to_generation.keys().collect()
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InferenceGraphBuilder {
    pedigree: Pedigree,
    entries_by_generation: Vec<BTreeSet<String>>,
    entries_to_generation: BTreeMap<String, usize>,
    leaves: BTreeSet<String>,
    root: String,
}

impl InferenceGraphBuilder {
    pub fn new() -> Self {
        InferenceGraphBuilder {
            pedigree: Pedigree::new(),
            entries_by_generation: Vec::new(),
            entries_to_generation: BTreeMap::new(),
            leaves: BTreeSet::new(),
            root: String::new(),
        }
    }

    pub fn finalize(self) -> InferenceGraph {
        InferenceGraph {
            pedigree: self.pedigree,
            entries_by_generation: self.entries_by_generation,
            entries_to_generation: self.entries_to_generation,
            leaves: self.leaves,
            root: self.root,
        }
    }

    pub fn pedigree(self, pedigree: Pedigree) -> Self {
        let mut igraph = self.clone();
        igraph.pedigree = pedigree;
        igraph
    }

    pub fn update_pedigree(self, id: String, origin: Origin) -> Self {
        let mut igraph = self.clone();
        igraph.pedigree = igraph.pedigree.insert(id, origin);
        igraph
    }

    pub fn entries_by_generation(self, entries_by_generation: Vec<BTreeSet<String>>) -> Self {
        let mut igraph = self.clone();
        igraph.entries_by_generation = entries_by_generation;
        igraph
    }

    pub fn extend_entries_by_generation(self, generation_idx: usize, entries: Vec<String>) -> Self {
        let mut entries_by_generation = self.entries_by_generation.clone();
        let new_entries: BTreeSet<String> = entries.into_iter().collect();

        if entries_by_generation.len() <= generation_idx {
            entries_by_generation.push(new_entries);
        } else {
            if let Some(generation) = entries_by_generation.get_mut(generation_idx) {
                generation.extend(new_entries);
            }
        }

        let mut igraph = self.clone();
        igraph.entries_by_generation = entries_by_generation;
        igraph
    }

    pub fn entries_to_generation(self, entries_to_generation: BTreeMap<String, usize>) -> Self {
        let mut igraph = self.clone();
        igraph.entries_to_generation = entries_to_generation;
        igraph
    }

    pub fn leaves(self, leaves: BTreeSet<String>) -> Self {
        let mut igraph = self.clone();
        igraph.leaves = leaves;
        igraph
    }

    pub fn update_leaves(self, id: String) -> Self {
        let mut igraph = self.clone();
        igraph.leaves.insert(id);
        igraph
    }

    pub fn root(self, root: String) -> Self {
        let mut igraph = self.clone();
        igraph.root = root;
        igraph
    }
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct InferenceChain {
    pub elements: Vec<(Vec<(String, Option<Origin>)>)>,
}

impl std::fmt::Display for InferenceChain {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", serde_json::to_string(&self).unwrap())
    }
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Pedigree {
    ancestors: BTreeMap<String, Origin>,
    descendents: BTreeMap<String, BTreeSet<String>>,
}

impl Pedigree {
    pub fn new() -> Self {
        Pedigree {
            ancestors: BTreeMap::new(),
            descendents: BTreeMap::new(),
        }
    }

    pub fn insert(&self, id: String, org: Origin) -> Self {
        let mut pedigree = self.clone();
        pedigree.insert_mut(id, org);
        pedigree
    }

    pub fn insert_mut(&mut self, id: String, org: Origin) {
        let source_id = org.source_id.clone();
        self.ancestors.insert(id.clone(), org);

        if !self.descendents.contains_key(&source_id) {
            self.descendents.insert(source_id.clone(), BTreeSet::new());
        }
        let mut inner_descendents = self.descendents.get_mut(&source_id).unwrap();
        inner_descendents.insert(id);
    }

    pub fn get_ancestor(&self, id: &String) -> Option<&Origin> {
        self.ancestors.get(id)
    }

    pub fn get_descendents(&self, id: &String) -> Option<&BTreeSet<String>> {
        self.descendents.get(id)
    }

    /// Remove all reference to the specified id
    pub fn purge(&self, id: &String) -> Self {
        let mut pedigree = self.clone();
        pedigree.purge_mut(id);
        pedigree
    }

    pub fn purge_mut(&mut self, id: &String) {
        self.ancestors.remove(id);

        let mut descendents = self.descendents.clone();

        for (ancestor_id, these_descendents) in self.descendents.iter() {
            if these_descendents.contains(id) {
                let mut these_descendents = these_descendents.clone();
                these_descendents.remove(id);
                descendents.insert(ancestor_id.clone(), these_descendents);
            }
        }

        self.descendents = descendents;
    }

    pub fn extract_inference_chain(&self, root: &String) -> InferenceChain {
        InferenceChain {
            elements: self.extract_inference_graph(root)
                .back_iter()
                .map(|(generation, _subsequent_generations)| {
                    generation.into_iter().map(|(ref id, ref origin)| ((*id).clone(), (*origin).cloned())).collect()
                })
                .collect(),
        }
    }

    pub fn extract_inference_graph(&self, root: &String) -> InferenceGraph {
        let builder = InferenceGraphBuilder::new().root(root.clone());

        // Note: we construct the entries_by_generation vector in reverse since we don't know in advance the how big it will be
        let mut builder = self.extract_inference_graph_helper(builder, vec![root], 0);

        // Reverse the order of the entries_by_generation so initial inferences are first and the root is last
        builder.entries_by_generation.reverse();
        let mut entries_to_generation: BTreeMap<String, usize> = BTreeMap::new();
        for (idx, entries) in builder.entries_by_generation.iter().enumerate() {
            for entry in entries.iter() {
                entries_to_generation.insert(entry.clone(), idx);
            }
        }
        builder.entries_to_generation = entries_to_generation;
        builder.finalize()
    }

    fn extract_inference_graph_helper(&self,
                                      builder: InferenceGraphBuilder,
                                      current_generation: Vec<&String>,
                                      generations_from_root: usize)
                                      -> InferenceGraphBuilder {
        if current_generation.len() > 0 {
            let builder = builder.extend_entries_by_generation(generations_from_root,
                                                               current_generation.iter().cloned().cloned().collect());
            current_generation.into_iter().fold(builder,
                                                |builder, current_id| match self.get_ancestor(current_id).cloned() {
                                                    None => builder,
                                                    Some(ref origin) => {
                                                        let builder = builder.update_pedigree(current_id.clone(), origin.clone());
                                                        let builder = if origin.args.is_empty() {
                                                            builder.update_leaves(current_id.clone())
                                                        } else {
                                                            builder
                                                        };
                                                        self.extract_inference_graph_helper(builder,
                                                                                origin.ancestors()
                                                                                .iter()
                                                                                .cloned()
                                                                                .collect(),
                                                                                generations_from_root + 1)
                                                    }
                                                })
        } else {
            builder
        }
    }

    pub fn render_inference_tree(&self,
                                 d_id: &String,
                                 root_renderer: &Fn(String) -> String,
                                 node_renderer: &Fn(String) -> String,
                                 relation_renderder: &Fn(String, String) -> String,
                                 render_type: RenderType)
                                 -> String {
        let s = match render_type {
            RenderType::Pedigree => self.render_inference_tree_pedigree(d_id, node_renderer),
            RenderType::Full => self.render_inference_tree_full(d_id, node_renderer, relation_renderder),
        };
        format!("graph \"inference chain for {}\" {{\n{}\n}}",
                root_renderer(d_id.clone()),
                s)
    }

    pub fn render_inference_tree_full(&self,
                                      d_id: &String,
                                      node_renderer: &Fn(String) -> String,
                                      relation_renderder: &Fn(String, String) -> String)
                                      -> String {
        let inf_graph = self.extract_inference_graph(d_id);
        let mut relationships: Vec<String> = Vec::new();

        for (current_generation, future_generations) in inf_graph.back_iter() {
            // For each generation
            for future_generation in future_generations.iter() {
                // For each following generation
                for (&(ref current_id, ref _origin), descendent_id) in
                    current_generation.iter()
                        .cartesian_product(future_generation.iter()) {
                    // For each pairing of current generation
                    relationships.push(format!("\"{}\" -- \"{}\" \"{}\"",
                                               node_renderer((*current_id).clone()),
                                               node_renderer(descendent_id.clone()),
                                               relation_renderder((*current_id).clone(), (*descendent_id).clone())));
                }
            }
        }
        relationships.iter().join(";\n")
    }

    pub fn render_inference_tree_pedigree(&self, d_id: &String, node_renderer: &Fn(String) -> String) -> String {
        let mut relationships: Vec<String> = Vec::new();
        if let Some(origin) = self.ancestors.get(d_id) {
            // Render the relationship to the parent actor
            relationships.push(format!(r#""{}" -- "{}""#,
                                       node_renderer(d_id.clone()),
                                       node_renderer(origin.source_id.clone())));
            for arg in origin.args.iter() {
                // Render the relationship to each parent actor argument
                relationships.push(format!(r#""{}" -- "{}""#,
                                           node_renderer(d_id.clone()),
                                           node_renderer(arg.clone())));
                // Render each parent actor argument's relationships
                relationships.push(self.render_inference_tree_pedigree(arg, node_renderer));
            }
        }
        relationships.iter().join(";\n")
    }
}
<commit_msg>Moved tests import<commit_after>use itertools::Itertools;
use serde_json;
use std;
use std::collections::btree_map::BTreeMap;
use std::collections::btree_set::BTreeSet;

#[derive(Clone, Copy, Debug)]
pub enum RenderType {
    Full,
    Pedigree,
}

/// Represent the origin of a particular datum
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Origin {
    /// What Actor does this Origin correspond to
    pub source_id: String,
    /// What datums did the source use to construct the entity that this Origin corresponds to
    pub args: Vec<String>,
}

impl std::fmt::Display for Origin {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", serde_json::to_string(&self).unwrap())
    }
}

impl Origin {
    pub fn new() -> Self {
        Origin {
            source_id: String::new(),
            args: Vec::new(),
        }
    }

    pub fn with_source(source: String) -> Self {
        Origin {
            source_id: source,
            args: Vec::new(),
        }
    }

    pub fn ancestors(&self) -> Vec<&String> {
        self.args.iter().chain(std::iter::once(&self.source_id)).collect()
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InferenceGraphBackwardIterator<'a> {
    inf_graph: &'a InferenceGraph,
    next_generation: Option<usize>,
}

impl<'a> Iterator for InferenceGraphBackwardIterator<'a> {
    type Item = (Vec<(&'a String, Option<&'a Origin>)>, Vec<&'a BTreeSet<String>>);

    fn next(&mut self) -> Option<(Vec<(&'a String, Option<&'a Origin>)>, Vec<&'a BTreeSet<String>>)> {
        let current_generation = self.next_generation;

        self.next_generation = self.next_generation.and_then(|idx| if idx == 0 { None } else { Some(idx - 1) });

        let construct_id_origin_tuple = |current_id| {
            (current_id,
             self.inf_graph
                 .pedigree
                 .get_ancestor(current_id))
        };

        let construct_id_origin_tuples_for_generation = |generation: &'a BTreeSet<String>| {
            generation.iter()
                .map(construct_id_origin_tuple)
                .collect()
        };

        current_generation.and_then(|generation_idx| {
            self.inf_graph.entries_by_generation.get(generation_idx).and_then(|generation| {
                Some((construct_id_origin_tuples_for_generation(generation), self.inf_graph.subsequent_inferences((generation_idx + 1))))
            })
        })
    }
}

/// Provide a convenient interface to a particular inference graph.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InferenceGraph {
    pedigree: Pedigree,
    /// Track which generation each datum was created in; the root ocurrs in the last generation
    entries_by_generation: Vec<BTreeSet<String>>,
    /// Inverse of entries_by_generation; map each id into the generation it was introduced in
    entries_to_generation: BTreeMap<String, usize>,
    /// The endpoint data for the inference graph
    /// For forward chaining, this is the initial data used in the inference.
    /// For backward chaining, this is the final grounded results.
    leaves: BTreeSet<String>,
    /// The goal of the infernece
    root: String,
}

impl<'a> InferenceGraph {
    pub fn new(root: String) -> Self {
        InferenceGraph {
            pedigree: Pedigree::new(),
            entries_by_generation: Vec::new(),
            entries_to_generation: BTreeMap::new(),
            leaves: BTreeSet::new(),
            root: root,
        }
    }

    pub fn back_iter(&self) -> InferenceGraphBackwardIterator {
        InferenceGraphBackwardIterator {
            inf_graph: self,
            next_generation: Some(self.entries_by_generation.len() - 1),
        }
    }

    pub fn root(&self) -> &String {
        &self.root
    }

    pub fn leaves(&'a self) -> &'a BTreeSet<String> {
        &self.leaves
    }

    pub fn ancestor(&self, id: &String) -> Option<&Origin> {
        self.pedigree.get_ancestor(id)
    }

    pub fn descendent_inferences(&'a self, id: &String) -> Option<&'a BTreeSet<String>> {
        /// Return datums derived from this one
        self.pedigree.get_descendents(id)
    }

    pub fn subsequent_inferences(&'a self, generation: usize) -> Vec<&'a BTreeSet<String>> {
        /// Return all datums derived in and after the specified generation
        let mut subsequent_inferences = Vec::new();

        for entries in self.entries_by_generation
            .iter()
            .skip(generation) {
            subsequent_inferences.push(entries);
        }

        subsequent_inferences
    }

    pub fn all_ids(&'a self) -> BTreeSet<&'a String> {
        self.entries_to_generation.keys().collect()
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InferenceGraphBuilder {
    pedigree: Pedigree,
    entries_by_generation: Vec<BTreeSet<String>>,
    entries_to_generation: BTreeMap<String, usize>,
    leaves: BTreeSet<String>,
    root: String,
}

impl InferenceGraphBuilder {
    pub fn new() -> Self {
        InferenceGraphBuilder {
            pedigree: Pedigree::new(),
            entries_by_generation: Vec::new(),
            entries_to_generation: BTreeMap::new(),
            leaves: BTreeSet::new(),
            root: String::new(),
        }
    }

    pub fn finalize(self) -> InferenceGraph {
        InferenceGraph {
            pedigree: self.pedigree,
            entries_by_generation: self.entries_by_generation,
            entries_to_generation: self.entries_to_generation,
            leaves: self.leaves,
            root: self.root,
        }
    }

    pub fn pedigree(self, pedigree: Pedigree) -> Self {
        let mut igraph = self.clone();
        igraph.pedigree = pedigree;
        igraph
    }

    pub fn update_pedigree(self, id: String, origin: Origin) -> Self {
        let mut igraph = self.clone();
        igraph.pedigree = igraph.pedigree.insert(id, origin);
        igraph
    }

    pub fn entries_by_generation(self, entries_by_generation: Vec<BTreeSet<String>>) -> Self {
        let mut igraph = self.clone();
        igraph.entries_by_generation = entries_by_generation;
        igraph
    }

    pub fn extend_entries_by_generation(self, generation_idx: usize, entries: Vec<String>) -> Self {
        let mut entries_by_generation = self.entries_by_generation.clone();
        let new_entries: BTreeSet<String> = entries.into_iter().collect();

        if entries_by_generation.len() <= generation_idx {
            entries_by_generation.push(new_entries);
        } else {
            if let Some(generation) = entries_by_generation.get_mut(generation_idx) {
                generation.extend(new_entries);
            }
        }

        let mut igraph = self.clone();
        igraph.entries_by_generation = entries_by_generation;
        igraph
    }

    pub fn entries_to_generation(self, entries_to_generation: BTreeMap<String, usize>) -> Self {
        let mut igraph = self.clone();
        igraph.entries_to_generation = entries_to_generation;
        igraph
    }

    pub fn leaves(self, leaves: BTreeSet<String>) -> Self {
        let mut igraph = self.clone();
        igraph.leaves = leaves;
        igraph
    }

    pub fn update_leaves(self, id: String) -> Self {
        let mut igraph = self.clone();
        igraph.leaves.insert(id);
        igraph
    }

    pub fn root(self, root: String) -> Self {
        let mut igraph = self.clone();
        igraph.root = root;
        igraph
    }
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct InferenceChain {
    pub elements: Vec<(Vec<(String, Option<Origin>)>)>,
}

impl std::fmt::Display for InferenceChain {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", serde_json::to_string(&self).unwrap())
    }
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Pedigree {
    ancestors: BTreeMap<String, Origin>,
    descendents: BTreeMap<String, BTreeSet<String>>,
}

impl Pedigree {
    pub fn new() -> Self {
        Pedigree {
            ancestors: BTreeMap::new(),
            descendents: BTreeMap::new(),
        }
    }

    pub fn insert(&self, id: String, org: Origin) -> Self {
        let mut pedigree = self.clone();
        pedigree.insert_mut(id, org);
        pedigree
    }

    pub fn insert_mut(&mut self, id: String, org: Origin) {
        let source_id = org.source_id.clone();
        self.ancestors.insert(id.clone(), org);

        if !self.descendents.contains_key(&source_id) {
            self.descendents.insert(source_id.clone(), BTreeSet::new());
        }
        let mut inner_descendents = self.descendents.get_mut(&source_id).unwrap();
        inner_descendents.insert(id);
    }

    pub fn get_ancestor(&self, id: &String) -> Option<&Origin> {
        self.ancestors.get(id)
    }

    pub fn get_descendents(&self, id: &String) -> Option<&BTreeSet<String>> {
        self.descendents.get(id)
    }

    /// Remove all reference to the specified id
    pub fn purge(&self, id: &String) -> Self {
        let mut pedigree = self.clone();
        pedigree.purge_mut(id);
        pedigree
    }

    pub fn purge_mut(&mut self, id: &String) {
        self.ancestors.remove(id);

        let mut descendents = self.descendents.clone();

        for (ancestor_id, these_descendents) in self.descendents.iter() {
            if these_descendents.contains(id) {
                let mut these_descendents = these_descendents.clone();
                these_descendents.remove(id);
                descendents.insert(ancestor_id.clone(), these_descendents);
            }
        }

        self.descendents = descendents;
    }

    pub fn extract_inference_chain(&self, root: &String) -> InferenceChain {
        InferenceChain {
            elements: self.extract_inference_graph(root)
                .back_iter()
                .map(|(generation, _subsequent_generations)| {
                    generation.into_iter().map(|(ref id, ref origin)| ((*id).clone(), (*origin).cloned())).collect()
                })
                .collect(),
        }
    }

    pub fn extract_inference_graph(&self, root: &String) -> InferenceGraph {
        let builder = InferenceGraphBuilder::new().root(root.clone());

        // Note: we construct the entries_by_generation vector in reverse since we don't know in advance the how big it will be
        let mut builder = self.extract_inference_graph_helper(builder, vec![root], 0);

        // Reverse the order of the entries_by_generation so initial inferences are first and the root is last
        builder.entries_by_generation.reverse();
        let mut entries_to_generation: BTreeMap<String, usize> = BTreeMap::new();
        for (idx, entries) in builder.entries_by_generation.iter().enumerate() {
            for entry in entries.iter() {
                entries_to_generation.insert(entry.clone(), idx);
            }
        }
        builder.entries_to_generation = entries_to_generation;
        builder.finalize()
    }

    fn extract_inference_graph_helper(&self,
                                      builder: InferenceGraphBuilder,
                                      current_generation: Vec<&String>,
                                      generations_from_root: usize)
                                      -> InferenceGraphBuilder {
        if current_generation.len() > 0 {
            let builder = builder.extend_entries_by_generation(generations_from_root,
                                                               current_generation.iter().cloned().cloned().collect());
            current_generation.into_iter().fold(builder,
                                                |builder, current_id| match self.get_ancestor(current_id).cloned() {
                                                    None => builder,
                                                    Some(ref origin) => {
                                                        let builder = builder.update_pedigree(current_id.clone(), origin.clone());
                                                        let builder = if origin.args.is_empty() {
                                                            builder.update_leaves(current_id.clone())
                                                        } else {
                                                            builder
                                                        };
                                                        self.extract_inference_graph_helper(builder,
                                                                                origin.ancestors()
                                                                                .iter()
                                                                                .cloned()
                                                                                .collect(),
                                                                                generations_from_root + 1)
                                                    }
                                                })
        } else {
            builder
        }
    }

    pub fn render_inference_tree(&self,
                                 d_id: &String,
                                 root_renderer: &Fn(String) -> String,
                                 node_renderer: &Fn(String) -> String,
                                 relation_renderder: &Fn(String, String) -> String,
                                 render_type: RenderType)
                                 -> String {
        let s = match render_type {
            RenderType::Pedigree => self.render_inference_tree_pedigree(d_id, node_renderer),
            RenderType::Full => self.render_inference_tree_full(d_id, node_renderer, relation_renderder),
        };
        format!("graph \"inference chain for {}\" {{\n{}\n}}",
                root_renderer(d_id.clone()),
                s)
    }

    pub fn render_inference_tree_full(&self,
                                      d_id: &String,
                                      node_renderer: &Fn(String) -> String,
                                      relation_renderder: &Fn(String, String) -> String)
                                      -> String {
        let inf_graph = self.extract_inference_graph(d_id);
        let mut relationships: Vec<String> = Vec::new();

        for (current_generation, future_generations) in inf_graph.back_iter() {
            // For each generation
            for future_generation in future_generations.iter() {
                // For each following generation
                for (&(ref current_id, ref _origin), descendent_id) in
                    current_generation.iter()
                        .cartesian_product(future_generation.iter()) {
                    // For each pairing of current generation
                    relationships.push(format!("\"{}\" -- \"{}\" \"{}\"",
                                               node_renderer((*current_id).clone()),
                                               node_renderer(descendent_id.clone()),
                                               relation_renderder((*current_id).clone(), (*descendent_id).clone())));
                }
            }
        }
        relationships.iter().join(";\n")
    }

    pub fn render_inference_tree_pedigree(&self, d_id: &String, node_renderer: &Fn(String) -> String) -> String {
        let mut relationships: Vec<String> = Vec::new();
        if let Some(origin) = self.ancestors.get(d_id) {
            // Render the relationship to the parent actor
            relationships.push(format!(r#""{}" -- "{}""#,
                                       node_renderer(d_id.clone()),
                                       node_renderer(origin.source_id.clone())));
            for arg in origin.args.iter() {
                // Render the relationship to each parent actor argument
                relationships.push(format!(r#""{}" -- "{}""#,
                                           node_renderer(d_id.clone()),
                                           node_renderer(arg.clone())));
                // Render each parent actor argument's relationships
                relationships.push(self.render_inference_tree_pedigree(arg, node_renderer));
            }
        }
        relationships.iter().join(";\n")
    }
}

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>use {
    super::render,
    crate::{
        component,
        layout::{self, Component, Layout, LayoutDirection, LayoutState},
        run::parser::{livesplit, llanfair, wsplit},
        tests_helper, Run, Timer,
    },
    image::Rgba,
    img_hash::{HasherConfig, ImageHash},
    std::{
        fs::{self, File},
        io::BufReader,
    },
};

fn file(path: &str) -> BufReader<File> {
    BufReader::new(File::open(path).unwrap())
}

fn lss(path: &str) -> Run {
    livesplit::parse(file(path), None).unwrap()
}

fn lsl(path: &str) -> Layout {
    layout::parser::parse(file(path)).unwrap()
}

#[test]
fn default() {
    let mut run = tests_helper::create_run(&["A", "B", "C", "D"]);
    run.set_game_name("Some Game Name");
    run.set_category_name("Some Category Name");
    run.set_attempt_count(1337);
    let mut timer = Timer::new(run).unwrap();
    let mut layout = Layout::default_layout();

    tests_helper::start_run(&mut timer);
    tests_helper::make_progress_run_with_splits_opt(&mut timer, &[Some(5.0), None, Some(10.0)]);

    let state = layout.state(&timer);

    check(&state, "luCCVRJIPLE=", "default");
}

#[test]
fn actual_split_file() {
    let run = lss("tests/run_files/livesplit1.0.lss");
    let timer = Timer::new(run).unwrap();
    let mut layout = Layout::default_layout();

    check(&layout.state(&timer), "jMDEKxBAPLE=", "actual_split_file");
}

#[test]
fn wsplit() {
    let run = wsplit::parse(file("tests/run_files/wsplit"), false).unwrap();
    let timer = Timer::new(run).unwrap();
    let mut layout = lsl("tests/layout_files/WSplit.lsl");

    check_dims(&layout.state(&timer), [250, 300], "j/jc3dn8t/c=", "wsplit");
}

#[test]
fn all_components() {
    let mut layout = lsl("tests/layout_files/All.lsl");
    let run = lss("tests/run_files/livesplit1.6_gametime.lss");
    let mut timer = Timer::new(run).unwrap();
    tests_helper::start_run(&mut timer);
    tests_helper::make_progress_run_with_splits_opt(
        &mut timer,
        &[Some(10.0), None, Some(20.0), Some(55.0)],
    );

    let state = layout.state(&timer);

    check_dims(&state, [300, 800], "4eH3scnJt7E=", "all_components");

    check_dims(&state, [150, 800], "SWPXSWFFa2s=", "all_components_thin");
}

#[test]
fn score_split() {
    use crate::{component::timer, layout::ComponentState};

    let run = lss("tests/run_files/livesplit1.0.lss");
    let timer = Timer::new(run).unwrap();
    let mut layout = Layout::default_layout();

    let mut state = layout.state(&timer);
    let prev_seg = state.components.pop().unwrap();
    state.components.pop();
    let mut timer_state = timer::Component::new().state(&timer, layout.general_settings());
    timer_state.time = "50346".into();
    timer_state.fraction = "PTS".into();
    state.components.push(ComponentState::Timer(timer_state));
    state.components.push(prev_seg);

    check_dims(&state, [300, 400], "jODEIwLQJDc=", "score_split");
}

#[test]
fn dark_layout() {
    let run = llanfair::parse(file("tests/run_files/llanfair")).unwrap();
    let timer = Timer::new(run).unwrap();
    let mut layout = lsl("tests/layout_files/dark.lsl");

    check(&layout.state(&timer), "D8IAQiBYxgM=", "dark_layout");
}

#[test]
fn subsplits_layout() {
    let run = lss("tests/run_files/Celeste - Any% (1.2.1.5).lss");
    let mut timer = Timer::new(run).unwrap();
    let mut layout = lsl("tests/layout_files/subsplits.lsl");

    tests_helper::start_run(&mut timer);
    tests_helper::make_progress_run_with_splits_opt(
        &mut timer,
        &[Some(10.0), None, Some(20.0), Some(55.0)],
    );

    check_dims(
        &layout.state(&timer),
        [300, 800],
        "8/Pz4/Pz/8c=",
        "subsplits_layout",
    );
}

#[test]
fn display_two_rows() {
    let timer = tests_helper::create_timer(&["A"]);
    let mut layout = Layout::new();
    let mut component = component::text::Component::new();
    let settings = component.settings_mut();
    settings.display_two_rows = true;
    settings.text =
        component::text::Text::Split(String::from("World Record"), String::from("Some Guy"));
    layout.push(component);

    let mut component = component::delta::Component::new();
    component.settings_mut().display_two_rows = true;
    layout.push(component);

    check_dims(
        &layout.state(&timer),
        [200, 100],
        "R00aWs1J9sE=",
        "display_two_rows",
    );
}

#[test]
fn single_line_title() {
    let mut run = tests_helper::create_run(&["A"]);
    run.set_game_name("Some Game");
    run.set_category_name("Some Category");
    run.set_attempt_count(1337);
    let timer = Timer::new(run).unwrap();
    let mut layout = Layout::new();
    let mut component = component::title::Component::new();
    let settings = component.settings_mut();
    settings.display_as_single_line = true;
    settings.show_attempt_count = true;
    settings.show_finished_runs_count = true;
    layout.push(component);

    check_dims(
        &layout.state(&timer),
        [150, 30],
        "QCRZz09hahA=",
        "single_line_title",
    );
}

#[test]
fn horizontal() {
    let run = lss("tests/run_files/Celeste - Any% (1.2.1.5).lss");
    let mut timer = Timer::new(run).unwrap();
    let mut layout = Layout::default_layout();
    layout.general_settings_mut().direction = LayoutDirection::Horizontal;
    match &mut layout.components[1] {
        Component::Splits(splits) => splits.settings_mut().visual_split_count = 4,
        _ => unreachable!("We wanted to configure the splits"),
    }
    layout.push(component::separator::Component::new());
    layout.push(component::graph::Component::new());
    layout.push(component::separator::Component::new());
    layout.push(Box::new(
        component::detailed_timer::Component::with_settings(component::detailed_timer::Settings {
            display_icon: true,
            ..Default::default()
        }),
    ));

    tests_helper::start_run(&mut timer);
    tests_helper::make_progress_run_with_splits_opt(
        &mut timer,
        &[Some(10.0), None, Some(20.0), Some(55.0)],
    );

    check_dims(
        &layout.state(&timer),
        [1500, 40],
        "YmJicmJSUmM=",
        "horizontal",
    );
}

fn get_comparison_tolerance() -> u32 {
    // Without MMX the floating point calculations don't follow IEEE 754, so the tests require a
    // tolerance that is greater than 0.
    // FIXME: We use SSE as an approximation for the cfg because MMX isn't supported by Rust yet.
    if cfg!(all(target_arch = "x86", not(target_feature = "sse"),)) {
        4
    } else {
        0
    }
}

fn check(state: &LayoutState, expected_hash_data: &str, name: &str) {
    check_dims(state, [300, 500], expected_hash_data, name);
}

fn check_dims(state: &LayoutState, dims: [usize; 2], expected_hash_data: &str, name: &str) {
    let image = render(state, dims);
    let hasher = HasherConfig::with_bytes_type::<[u8; 8]>().to_hasher();

    let calculated_hash = hasher.hash_image(&image);
    let calculated_hash_data = calculated_hash.to_base64();
    let expected_hash = ImageHash::<[u8; 8]>::from_base64(expected_hash_data).unwrap();
    let distance = calculated_hash.dist(&expected_hash);

    fs::create_dir_all("target/renders").ok();

    let path = format!(
        "target/renders/{}_{}.png",
        name,
        calculated_hash_data.replace("/", "$"),
    );
    image.save(&path).ok();

    if distance > get_comparison_tolerance() {
        fs::create_dir_all("target/renders/diff").ok();

        let expected_path = format!(
            "target/renders/{}_{}.png",
            name,
            expected_hash_data.replace("/", "$"),
        );
        if let Ok(expected_image) = image::open(expected_path) {
            let mut expected_image = expected_image.to_rgba();
            for (x, y, Rgba([r, g, b, a])) in expected_image.enumerate_pixels_mut() {
                if x < image.width() && y < image.height() {
                    let Rgba([r2, g2, b2, a2]) = image.get_pixel(x, y);
                    *r = (*r as i16).wrapping_sub(*r2 as i16).abs() as u8;
                    *g = (*g as i16).wrapping_sub(*g2 as i16).abs() as u8;
                    *b = (*b as i16).wrapping_sub(*b2 as i16).abs() as u8;
                    *a = (*a).max(*a2);
                }
            }
            let diff_path = format!("target/renders/diff/{}.png", name);
            expected_image.save(&diff_path).ok();
        }

        panic!(
            "Render mismatch for {} expected: {}, actual: {}, distance: {}",
            name, expected_hash_data, calculated_hash_data, distance
        );
    }
}
<commit_msg>Increase Comparison Tolerance for Rendering Tests<commit_after>use {
    super::render,
    crate::{
        component,
        layout::{self, Component, Layout, LayoutDirection, LayoutState},
        run::parser::{livesplit, llanfair, wsplit},
        tests_helper, Run, Timer,
    },
    image::Rgba,
    img_hash::{HasherConfig, ImageHash},
    std::{
        fs::{self, File},
        io::BufReader,
    },
};

fn file(path: &str) -> BufReader<File> {
    BufReader::new(File::open(path).unwrap())
}

fn lss(path: &str) -> Run {
    livesplit::parse(file(path), None).unwrap()
}

fn lsl(path: &str) -> Layout {
    layout::parser::parse(file(path)).unwrap()
}

#[test]
fn default() {
    let mut run = tests_helper::create_run(&["A", "B", "C", "D"]);
    run.set_game_name("Some Game Name");
    run.set_category_name("Some Category Name");
    run.set_attempt_count(1337);
    let mut timer = Timer::new(run).unwrap();
    let mut layout = Layout::default_layout();

    tests_helper::start_run(&mut timer);
    tests_helper::make_progress_run_with_splits_opt(&mut timer, &[Some(5.0), None, Some(10.0)]);

    let state = layout.state(&timer);

    check(&state, "luCCVRJIPLE=", "default");
}

#[test]
fn actual_split_file() {
    let run = lss("tests/run_files/livesplit1.0.lss");
    let timer = Timer::new(run).unwrap();
    let mut layout = Layout::default_layout();

    check(&layout.state(&timer), "jMDEKxBAPLE=", "actual_split_file");
}

#[test]
fn wsplit() {
    let run = wsplit::parse(file("tests/run_files/wsplit"), false).unwrap();
    let timer = Timer::new(run).unwrap();
    let mut layout = lsl("tests/layout_files/WSplit.lsl");

    check_dims(&layout.state(&timer), [250, 300], "j/jc3dn8t/c=", "wsplit");
}

#[test]
fn all_components() {
    let mut layout = lsl("tests/layout_files/All.lsl");
    let run = lss("tests/run_files/livesplit1.6_gametime.lss");
    let mut timer = Timer::new(run).unwrap();
    tests_helper::start_run(&mut timer);
    tests_helper::make_progress_run_with_splits_opt(
        &mut timer,
        &[Some(10.0), None, Some(20.0), Some(55.0)],
    );

    let state = layout.state(&timer);

    check_dims(&state, [300, 800], "4eH3scnJt7E=", "all_components");

    check_dims(&state, [150, 800], "SWPXSWFFa2s=", "all_components_thin");
}

#[test]
fn score_split() {
    use crate::{component::timer, layout::ComponentState};

    let run = lss("tests/run_files/livesplit1.0.lss");
    let timer = Timer::new(run).unwrap();
    let mut layout = Layout::default_layout();

    let mut state = layout.state(&timer);
    let prev_seg = state.components.pop().unwrap();
    state.components.pop();
    let mut timer_state = timer::Component::new().state(&timer, layout.general_settings());
    timer_state.time = "50346".into();
    timer_state.fraction = "PTS".into();
    state.components.push(ComponentState::Timer(timer_state));
    state.components.push(prev_seg);

    check_dims(&state, [300, 400], "jODEIwLQJDc=", "score_split");
}

#[test]
fn dark_layout() {
    let run = llanfair::parse(file("tests/run_files/llanfair")).unwrap();
    let timer = Timer::new(run).unwrap();
    let mut layout = lsl("tests/layout_files/dark.lsl");

    check(&layout.state(&timer), "D8IAQiBYxgM=", "dark_layout");
}

#[test]
fn subsplits_layout() {
    let run = lss("tests/run_files/Celeste - Any% (1.2.1.5).lss");
    let mut timer = Timer::new(run).unwrap();
    let mut layout = lsl("tests/layout_files/subsplits.lsl");

    tests_helper::start_run(&mut timer);
    tests_helper::make_progress_run_with_splits_opt(
        &mut timer,
        &[Some(10.0), None, Some(20.0), Some(55.0)],
    );

    check_dims(
        &layout.state(&timer),
        [300, 800],
        "8/Pz4/Pz/8c=",
        "subsplits_layout",
    );
}

#[test]
fn display_two_rows() {
    let timer = tests_helper::create_timer(&["A"]);
    let mut layout = Layout::new();
    let mut component = component::text::Component::new();
    let settings = component.settings_mut();
    settings.display_two_rows = true;
    settings.text =
        component::text::Text::Split(String::from("World Record"), String::from("Some Guy"));
    layout.push(component);

    let mut component = component::delta::Component::new();
    component.settings_mut().display_two_rows = true;
    layout.push(component);

    check_dims(
        &layout.state(&timer),
        [200, 100],
        "R00aWs1J9sE=",
        "display_two_rows",
    );
}

#[test]
fn single_line_title() {
    let mut run = tests_helper::create_run(&["A"]);
    run.set_game_name("Some Game");
    run.set_category_name("Some Category");
    run.set_attempt_count(1337);
    let timer = Timer::new(run).unwrap();
    let mut layout = Layout::new();
    let mut component = component::title::Component::new();
    let settings = component.settings_mut();
    settings.display_as_single_line = true;
    settings.show_attempt_count = true;
    settings.show_finished_runs_count = true;
    layout.push(component);

    check_dims(
        &layout.state(&timer),
        [150, 30],
        "QCRZz09hahA=",
        "single_line_title",
    );
}

#[test]
fn horizontal() {
    let run = lss("tests/run_files/Celeste - Any% (1.2.1.5).lss");
    let mut timer = Timer::new(run).unwrap();
    let mut layout = Layout::default_layout();
    layout.general_settings_mut().direction = LayoutDirection::Horizontal;
    match &mut layout.components[1] {
        Component::Splits(splits) => splits.settings_mut().visual_split_count = 4,
        _ => unreachable!("We wanted to configure the splits"),
    }
    layout.push(component::separator::Component::new());
    layout.push(component::graph::Component::new());
    layout.push(component::separator::Component::new());
    layout.push(Box::new(
        component::detailed_timer::Component::with_settings(component::detailed_timer::Settings {
            display_icon: true,
            ..Default::default()
        }),
    ));

    tests_helper::start_run(&mut timer);
    tests_helper::make_progress_run_with_splits_opt(
        &mut timer,
        &[Some(10.0), None, Some(20.0), Some(55.0)],
    );

    check_dims(
        &layout.state(&timer),
        [1500, 40],
        "YmJicmJSUmM=",
        "horizontal",
    );
}

fn get_comparison_tolerance() -> u32 {
    // Without MMX the floating point calculations don't follow IEEE 754, so the tests require a
    // tolerance that is greater than 0.
    // FIXME: We use SSE as an approximation for the cfg because MMX isn't supported by Rust yet.
    if cfg!(all(target_arch = "x86", not(target_feature = "sse"))) {
        4
    } else {
        1
    }
}

fn check(state: &LayoutState, expected_hash_data: &str, name: &str) {
    check_dims(state, [300, 500], expected_hash_data, name);
}

fn check_dims(state: &LayoutState, dims: [usize; 2], expected_hash_data: &str, name: &str) {
    let image = render(state, dims);
    let hasher = HasherConfig::with_bytes_type::<[u8; 8]>().to_hasher();

    let calculated_hash = hasher.hash_image(&image);
    let calculated_hash_data = calculated_hash.to_base64();
    let expected_hash = ImageHash::<[u8; 8]>::from_base64(expected_hash_data).unwrap();
    let distance = calculated_hash.dist(&expected_hash);

    fs::create_dir_all("target/renders").ok();

    let path = format!(
        "target/renders/{}_{}.png",
        name,
        calculated_hash_data.replace("/", "$"),
    );
    image.save(&path).ok();

    if distance > get_comparison_tolerance() {
        fs::create_dir_all("target/renders/diff").ok();

        let expected_path = format!(
            "target/renders/{}_{}.png",
            name,
            expected_hash_data.replace("/", "$"),
        );
        if let Ok(expected_image) = image::open(expected_path) {
            let mut expected_image = expected_image.to_rgba();
            for (x, y, Rgba([r, g, b, a])) in expected_image.enumerate_pixels_mut() {
                if x < image.width() && y < image.height() {
                    let Rgba([r2, g2, b2, a2]) = image.get_pixel(x, y);
                    *r = (*r as i16).wrapping_sub(*r2 as i16).abs() as u8;
                    *g = (*g as i16).wrapping_sub(*g2 as i16).abs() as u8;
                    *b = (*b as i16).wrapping_sub(*b2 as i16).abs() as u8;
                    *a = (*a).max(*a2);
                }
            }
            let diff_path = format!("target/renders/diff/{}.png", name);
            expected_image.save(&diff_path).ok();
        }

        panic!(
            "Render mismatch for {} expected: {}, actual: {}, distance: {}",
            name, expected_hash_data, calculated_hash_data, distance
        );
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::marker::PhantomData;

use hyper::method::Method;
use serde::json;

use response;
use request::RequestBuilder;
use request::DoRequest;

// name (true)  String           The human-readable string you wish to use when displaying the
//                               Droplet name. The name, if set to a domain name managed in the
//                               DigitalOcean DNS management system, will configure a PTR record
//                               for the Droplet. The name set during creation will also determine
//                               the hostname for the Droplet in its internal configuration.
// region (true) String          The unique slug id for the region that you wish to deploy in
// size (true)   String          The unique slug identifier for the size that you wish to select
//                               for this Droplet.
// image         number (if using an image ID), or String (if using a public image slug) The image ID of a public or private image, or the unique slug identifier for a public image. This image will be the base image for your Droplet.    true
// ssh_keys      Array           An array containing the IDs or fingerprints of the SSH keys that you wish to embed in the Droplet's root account upon creation.
// backups       Boolean         A boolean indicating whether automated backups should be enabled for the Droplet. Automated backups can only be enabled when the Droplet is created.
// ipv6          Boolean         A boolean indicating whether IPv6 is enabled on the Droplet.
// private_networking  Boolean  A boolean indicating whether private networking is enabled for the Droplet. Private networking is currently only available in certain regions.
// user_data     String          A string of the desired User Data for the Droplet. User Data is currently only available in regions with metadata listed in their features.
#[derive(Serialize)]
pub struct Droplet {
    pub name: String,
    pub region: String,
    pub size: String,
    pub image: String,
    pub ssh_keys: Option<Vec<String>>,
    pub backups: bool,
    pub ipv6: bool,
    pub private_net: bool,
    pub user_data: Option<String>
}

impl fmt::Display for Droplet {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
            "Name: {}\n\
             Region: {}\n\
             Size: {}\n\
             Image: {}\n\
             SSH Keys: {}\n\
             Backups Enabled: {}\n\
             IPv6 Enabled: {}\n\
             Private Networking Enabled: {}\n\
             User Data: {}\n",
             self.name,
             self.region,
             self.size,
             self.image,
             if let Some(ref v) = self.ssh_keys {
                v.iter().fold(String::new(), |acc, s| acc + &format!(" {},", s)[..])
             } else {
                "None".to_owned()
             },
             self.backups,
             self.ipv6,
             self.private_net,
             if let Some(d) = self.user_data.clone() {
                d
             } else {
                "None".to_owned()
             }
        )
    }
}

impl<'t> RequestBuilder<'t, response::Droplet> {
    pub fn update(&self, id: &str) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn disable_backups(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn reboot(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn power_cycle(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn shutdown(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn power_off(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn power_on(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn restore(&self, img: &str) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn reset_password(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn resize(&self, size: &str, disk: bool) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn rebuild(&self, img: &str) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn rename(&self, name: &str) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn change_kernel(&self, kernel: &str) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn enable_ipv6(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn enable_private_networking(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn snapshot(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn upgrade(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn action(&self, id: &str) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn kernels(mut self) -> RequestBuilder<'t, response::Kernels> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/kernels"
        self.url.push('/');
        self.url.push_str("kernels");
        RequestBuilder::new(self.auth, self.url)
    }
    pub fn snapshots(mut self) -> RequestBuilder<'t, response::Snapshots> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/snapshots"
        self.url.push('/');
        self.url.push_str("snapshots");
        RequestBuilder::new(self.auth, self.url)
    }
    pub fn backups(mut self) -> RequestBuilder<'t, response::Backups> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/backups"
        self.url.push('/');
        self.url.push_str("backups");
        RequestBuilder::new(self.auth, self.url)
    }
    pub fn actions(mut self) -> RequestBuilder<'t, response::Actions> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        self.url.push('/');
        self.url.push_str("actions");
        RequestBuilder::new(self.auth, self.url)
    }
    pub fn delete(self) -> RequestBuilder<'t, response::HeaderOnly> {
        // DELETE: "https://api.digitalocean.com/v2/droplets/$ID"
        RequestBuilder {
            method: Method::Delete,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: None
        }
    }
    pub fn show(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn neighbors(mut self) -> RequestBuilder<'t, response::Droplets> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/neighbors"
        self.url.push('/');
        self.url.push_str("neighbors");
        RequestBuilder::new(self.auth, self.url)
    }
}

impl<'t> RequestBuilder<'t, response::Droplets> {
    pub fn create(self, droplet: &Droplet) -> RequestBuilder<'t, response::Droplet> {
        // POST:
        // body:
        //      "name" : ""             // true
        //      "region" : ""           // true
        //      "size" : ""             // true
        //      "image" : ""            // true
        //      "ssh_keys" : ""
        //      "backups" : ""
        //      "ipv6" : ""
        //      "private_networking" : ""
        //      "user_data" : ""

        // FIXME: don't unwrap()
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(json::to_string(droplet).ok().unwrap())
        }

        // let mut hm = HashMap::new();
        // hm.insert("name", droplet.name.clone());
        // hm.insert("region", droplet.region.clone());
        // hm.insert("size", droplet.size.clone());
        // hm.insert("image", droplet.image.clone());
        // if let Some(ref n) = rec.name {
        //     hm.insert("name", n.to_owned());
        // }
        // if let Some(ref d) = rec.data {
        //     hm.insert("data", d.to_owned());
        // }
        // if let Some(p) = rec.priority {
        //     hm.insert("priority", p.to_string());
        // }
        // if let Some(p) = rec.port {
        //     hm.insert("name", p.to_string());
        // }
        // if let Some(w) = rec.weight {
        //     hm.insert("name", w.to_string());
        // }
    }
    pub fn neighbors(self) -> RequestBuilder<'t, response::Neighbors> {
        // GET: "https://api.digitalocean.com/v2/reports/droplet_neighbors"
        RequestBuilder::new(self.auth, "https://api.digitalocean.com/v2/reports/droplet_neighbors")
    }
    pub fn upgrades(self) -> RequestBuilder<'t, response::DropletUpgrades> {
        // GET: "https://api.digitalocean.com/v2/droplet_upgrades"
        RequestBuilder::new(self.auth, "https://api.digitalocean.com/v2/droplet_upgrades")
    }
}

impl<'t> DoRequest<response::Droplet> for RequestBuilder<'t, response::Droplet> {}

<commit_msg>feat(Droplet Actions): implements droplet actions API<commit_after>use std::fmt;
use std::marker::PhantomData;

use hyper::method::Method;
use serde::json;

use response;
use request::RequestBuilder;
use request::DoRequest;

// name (true)  String           The human-readable string you wish to use when displaying the
//                               Droplet name. The name, if set to a domain name managed in the
//                               DigitalOcean DNS management system, will configure a PTR record
//                               for the Droplet. The name set during creation will also determine
//                               the hostname for the Droplet in its internal configuration.
// region (true) String          The unique slug id for the region that you wish to deploy in
// size (true)   String          The unique slug identifier for the size that you wish to select
//                               for this Droplet.
// image         number (if using an image ID), or String (if using a public image slug) The image ID of a public or private image, or the unique slug identifier for a public image. This image will be the base image for your Droplet.    true
// ssh_keys      Array           An array containing the IDs or fingerprints of the SSH keys that you wish to embed in the Droplet's root account upon creation.
// backups       Boolean         A boolean indicating whether automated backups should be enabled for the Droplet. Automated backups can only be enabled when the Droplet is created.
// ipv6          Boolean         A boolean indicating whether IPv6 is enabled on the Droplet.
// private_networking  Boolean  A boolean indicating whether private networking is enabled for the Droplet. Private networking is currently only available in certain regions.
// user_data     String          A string of the desired User Data for the Droplet. User Data is currently only available in regions with metadata listed in their features.
#[derive(Serialize)]
pub struct Droplet {
    pub name: String,
    pub region: String,
    pub size: String,
    pub image: String,
    pub ssh_keys: Option<Vec<String>>,
    pub backups: bool,
    pub ipv6: bool,
    pub private_networking: bool,
    pub user_data: Option<String>
}

#[derive(Serialize)]
struct DropletWithId {
    pub name: String,
    pub region: String,
    pub size: String,
    pub image: u64,
    pub ssh_keys: Option<Vec<String>>,
    pub backups: bool,
    pub ipv6: bool,
    pub private_networking: bool,
    pub user_data: Option<String>
}

impl DropletWithId {
    fn from_droplet(d: &Droplet) -> DropletWithId {
        DropletWithId {
            name: d.name().clone(),
            region: d.region.clone(),
            size: d.size.clone(),
            image: &d.image[..].parse().ok().unwrap(),
            ssh_keys: d.ssh_keys.clone(),
            backups: d.backups,
            ipv6: d.ipv6,
            private_networking: d.private_networking,
            user_data: d.user_data.clone()
        }
    }
}

impl fmt::Display for Droplet {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
            "Name: {}\n\
             Region: {}\n\
             Size: {}\n\
             Image: {}\n\
             SSH Keys: {}\n\
             Backups Enabled: {}\n\
             IPv6 Enabled: {}\n\
             Private Networking Enabled: {}\n\
             User Data: {}\n",
             self.name,
             self.region,
             self.size,
             self.image,
             if let Some(ref v) = self.ssh_keys {
                v.iter().fold(String::new(), |acc, s| acc + &format!(" {},", s)[..])
             } else {
                "None".to_owned()
             },
             self.backups,
             self.ipv6,
             self.private_net,
             if let Some(d) = self.user_data.clone() {
                d
             } else {
                "None".to_owned()
             }
        )
    }
}

impl<'t> RequestBuilder<'t, response::Droplet> {
    pub fn disable_backups(mut self) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "disable_backups"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"disable_backups\"}}"))
        }
    }
    pub fn reboot(self) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "reboot"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"reboot\"}}"))
        }
    }
    pub fn power_cycle(self) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "power_cycle"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"power_cycle\"}}"))
        }
    }
    pub fn shutdown(self) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "shutdown"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"shutdown\"}}"))
        }
    }
    pub fn power_off(self) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "power_off"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"power_off\"}}"))
        }
    }
    pub fn power_on(self) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "power_on"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"power_on\"}}"))
        }
    }
    pub fn restore(self, img: &str) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "restore"
        //      "image": 12356          // number if ID, string if slug
        self.url.push_str("/actions");
        let image = match img.parse::<u64>() {
            Ok(n)  => img.to_string(),
            Err(_) => format!("\"{}\"", img)
        };
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"restore\",\"image\":{}}}", image))
        }
    }
    pub fn reset_password(self) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "password_reset"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"password_reset\"}}"))
        }
    }
    pub fn resize(self, size: &str, disk: bool) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "resize"
        //      "disk" : bool
        //      "size" : "1gb"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"resize\",\"disk\":{},\"size\":{:?}}}", disk.to_string(), size))
        }
    }
    pub fn rebuild(self, img: &str) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "rebuild"
        //      "image": 12356          // number if ID, string if slug
        self.url.push_str("/actions");
        let image = match img.parse::<u64>() {
            Ok(n)  => img.to_string(),
            Err(_) => format!("\"{}\"", img)
        };
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"rebuild\",\"image\":{}}}", image))
        }
    }
    pub fn rename(self, name: &str) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "rename"
        //      "name" : "some name"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"rename\",\"name\":{:?}}}", name))
        }
    }
    pub fn change_kernel(self, kernel: &str) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "change_kernel"
        //      "kernel" :  1234566
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"change_kernel\",\"kernel\":{}}}", kernel))
        }
    }
    pub fn enable_ipv6(self) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "enable_ipv6"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"enable_ipv6\"}}"))
        }
    }
    pub fn enable_private_networking(self) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "enable_private_networking"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"enable_private_networking\"}}"))
        }
    }
    pub fn snapshot(self, name: &str) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "snapshot"
        //      "name" : "some name"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"snapshot\",\"name\":{:?}}}", name))
        }
    }
    pub fn upgrade(self) -> RequestBuilder<'t, response::Action> {
        // POST: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        // body:
        //      "type" : "upgrade"
        self.url.push_str("/actions");
        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(format!("{{\"type\":\"upgrade\"}}"))
        }
    }
    pub fn action(mut self, id: &str) -> RequestBuilder<'t, response::Action> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/kernels"
        self.url.push('/');
        self.url.push_str(id);
        RequestBuilder::new(self.auth, self.url)
    }
    pub fn kernels(mut self) -> RequestBuilder<'t, response::Kernels> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/kernels"
        self.url.push_str("/kernels");
        RequestBuilder::new(self.auth, self.url)
    }
    pub fn snapshots(mut self) -> RequestBuilder<'t, response::Snapshots> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/snapshots"
        self.url.push_str("/snapshots");
        RequestBuilder::new(self.auth, self.url)
    }
    pub fn backups(mut self) -> RequestBuilder<'t, response::Backups> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/backups"
        self.url.push_str("/backups");
        RequestBuilder::new(self.auth, self.url)
    }
    pub fn actions(mut self) -> RequestBuilder<'t, response::Actions> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/actions"
        self.url.push_str("/actions");
        RequestBuilder::new(self.auth, self.url)
    }
    pub fn delete(self) -> RequestBuilder<'t, response::HeaderOnly> {
        // DELETE: "https://api.digitalocean.com/v2/droplets/$ID"
        RequestBuilder {
            method: Method::Delete,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: None
        }
    }
    pub fn show(&self) -> RequestBuilder<'t, response::Droplet> {
        unimplemented!()
    }
    pub fn neighbors(mut self) -> RequestBuilder<'t, response::Droplets> {
        // GET: "https://api.digitalocean.com/v2/droplets/$ID/neighbors"
        self.url.push_str("/neighbors");
        RequestBuilder::new(self.auth, self.url)
    }
}

impl<'t> RequestBuilder<'t, response::Droplets> {
    pub fn create(self, droplet: &Droplet) -> RequestBuilder<'t, response::Droplet> {
        // POST:
        // body:
        //      "name" : ""             // true
        //      "region" : ""           // true
        //      "size" : ""             // true
        //      "image" : ""            // true
        //      "ssh_keys" : ""
        //      "backups" : ""
        //      "ipv6" : ""
        //      "private_networking" : ""
        //      "user_data" : ""

        // FIXME: don't unwrap()

        if &droplet.image[..].parse::<u64>().is_ok() {
            let d = DropletWithId::from_droplet(droplet);
            return RequestBuilder {
                method: Method::Post,
                auth: self.auth,
                url: self.url,
                resp_t: PhantomData,
                body: Some(json::to_string(&d).ok().unwrap())
            }
        }

        RequestBuilder {
            method: Method::Post,
            auth: self.auth,
            url: self.url,
            resp_t: PhantomData,
            body: Some(json::to_string(droplet).ok().unwrap())
        }

    }
    pub fn neighbors(self) -> RequestBuilder<'t, response::Neighbors> {
        // GET: "https://api.digitalocean.com/v2/reports/droplet_neighbors"
        RequestBuilder::new(self.auth, "https://api.digitalocean.com/v2/reports/droplet_neighbors")
    }
    pub fn upgrades(self) -> RequestBuilder<'t, response::DropletUpgrades> {
        // GET: "https://api.digitalocean.com/v2/droplet_upgrades"
        RequestBuilder::new(self.auth, "https://api.digitalocean.com/v2/droplet_upgrades")
    }
}

impl<'t> DoRequest<response::Droplet> for RequestBuilder<'t, response::Droplet> {}
<|endoftext|>


<commit_before>//! Agent policy module.
//!
//! This module contains [fixed](fixed/index.html) and [parameterised](parameterised/index.html)
//! policies for reinforcement learning control problems. A policy is considered to be either
//! deterministic or stochastic, for which we have the following definitions, respectively:
//! 1) _π(X) -> U_;
//! 2) _π(X, U) -> R_, or equivalently, _π(x) -> Ω(U)_;
//!
//! where _X_ and _U_ are the state and action spaces, respectively; _R_ is the set of real values;
//! and _Ω(U)_ is the set of probability measures on the action set _U_. In general, deterministic
//! policies may be considered a special case of stochastic policies in which all probability mass
//! is placed on a single action _u'_ for any given state _x_. For continuous policies, this can be
//! seen as a dirac delta distribution, _δ(u' - u)_.
use crate::{
    core::*,
    domains::Transition,
    fa::Parameterised,
};
use rand::{thread_rng, Rng, seq::SliceRandom};

#[allow(dead_code)]
#[inline]
pub(self) fn sample_probs(probabilities: &[f64]) -> usize {
    sample_probs_with_rng(&mut thread_rng(), probabilities)
}

#[inline]
pub(self) fn sample_probs_with_rng(rng: &mut impl Rng, probabilities: &[f64]) -> usize {
    let r = rng.gen::<f64>();

    match probabilities.into_iter().scan(0.0, |state, &p| {
        *state = *state + p;

        Some(*state)
    }).position(|p| p > r) {
        Some(index) => index,
        None => probabilities.len() - 1,
    }
}

/// Policy trait for functions that define a probability distribution over actions.
pub trait Policy<S>: Algorithm {
    type Action;

    /// Sample the (possibly stochastic) policy distribution for a given input.
    fn sample(&mut self, input: &S) -> Self::Action {
        self.mpa(input)
    }

    /// Return the "most probable action" according to the policy distribution, if well-defined.
    fn mpa(&mut self, input: &S) -> Self::Action {
        unimplemented!()
    }

    /// Return the probability of selecting an action for a given input.
    fn probability(&mut self, input: &S, a: Self::Action) -> f64;
}

/// Trait for policies that are defined on a finite action space.
pub trait FinitePolicy<S>: Policy<S, Action = usize> {
    fn n_actions(&self) -> usize;

    /// Return the probability of selecting each action for a given input.
    fn probabilities(&mut self, input: &S) -> Vector<f64>;
}

/// Trait for policies that have a differentiable representation.
pub trait DifferentiablePolicy<S>: Policy<S> {
    /// Compute the derivative of the log probability for a single action.
    fn grad_log(&self, input: &S, a: Self::Action) -> Matrix<f64>;
}

/// Trait for policies that are parameterised by a vector of weights.
pub trait ParameterisedPolicy<S>: Policy<S> + Parameterised {
    /// Update the weights in the direction of an error for a given state and action.
    fn update(&mut self, input: &S, a: Self::Action, error: f64);

    /// Update the weights directly using an update matrix.
    fn update_raw(&mut self, errors: Matrix<f64>);
}

pub mod fixed;
pub mod parameterised;
<commit_msg>style(policies): replace unused argument with _<commit_after>//! Agent policy module.
//!
//! This module contains [fixed](fixed/index.html) and [parameterised](parameterised/index.html)
//! policies for reinforcement learning control problems. A policy is considered to be either
//! deterministic or stochastic, for which we have the following definitions, respectively:
//! 1) _π(X) -> U_;
//! 2) _π(X, U) -> R_, or equivalently, _π(x) -> Ω(U)_;
//!
//! where _X_ and _U_ are the state and action spaces, respectively; _R_ is the set of real values;
//! and _Ω(U)_ is the set of probability measures on the action set _U_. In general, deterministic
//! policies may be considered a special case of stochastic policies in which all probability mass
//! is placed on a single action _u'_ for any given state _x_. For continuous policies, this can be
//! seen as a dirac delta distribution, _δ(u' - u)_.
use crate::{
    core::*,
    domains::Transition,
    fa::Parameterised,
};
use rand::{thread_rng, Rng, seq::SliceRandom};

#[allow(dead_code)]
#[inline]
pub(self) fn sample_probs(probabilities: &[f64]) -> usize {
    sample_probs_with_rng(&mut thread_rng(), probabilities)
}

#[inline]
pub(self) fn sample_probs_with_rng(rng: &mut impl Rng, probabilities: &[f64]) -> usize {
    let r = rng.gen::<f64>();

    match probabilities.into_iter().scan(0.0, |state, &p| {
        *state = *state + p;

        Some(*state)
    }).position(|p| p > r) {
        Some(index) => index,
        None => probabilities.len() - 1,
    }
}

/// Policy trait for functions that define a probability distribution over actions.
pub trait Policy<S>: Algorithm {
    type Action;

    /// Sample the (possibly stochastic) policy distribution for a given input.
    fn sample(&mut self, input: &S) -> Self::Action {
        self.mpa(input)
    }

    /// Return the "most probable action" according to the policy distribution, if well-defined.
    fn mpa(&mut self, _: &S) -> Self::Action {
        unimplemented!()
    }

    /// Return the probability of selecting an action for a given input.
    fn probability(&mut self, input: &S, a: Self::Action) -> f64;
}

/// Trait for policies that are defined on a finite action space.
pub trait FinitePolicy<S>: Policy<S, Action = usize> {
    fn n_actions(&self) -> usize;

    /// Return the probability of selecting each action for a given input.
    fn probabilities(&mut self, input: &S) -> Vector<f64>;
}

/// Trait for policies that have a differentiable representation.
pub trait DifferentiablePolicy<S>: Policy<S> {
    /// Compute the derivative of the log probability for a single action.
    fn grad_log(&self, input: &S, a: Self::Action) -> Matrix<f64>;
}

/// Trait for policies that are parameterised by a vector of weights.
pub trait ParameterisedPolicy<S>: Policy<S> + Parameterised {
    /// Update the weights in the direction of an error for a given state and action.
    fn update(&mut self, input: &S, a: Self::Action, error: f64);

    /// Update the weights directly using an update matrix.
    fn update_raw(&mut self, errors: Matrix<f64>);
}

pub mod fixed;
pub mod parameterised;
<|endoftext|>


<commit_before>use std::convert::Into;

use libimagstore::store::Entry;

#[derive(PartialOrd, Ord, Eq, PartialEq, Clone, Debug)]
pub struct Link {
    link: String
}

impl Link {

    pub fn new(s: String) -> Link {
        Link { link: s }
    }

}

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Links {
    links: Vec<Link>,
}

impl Links {

    pub fn new(s: Vec<Link>) -> Links {
        Links { links: s }
    }

}

impl Into<String> for Link {

    fn into(self) -> String {
        self.link
    }

}

impl Into<Vec<Link>> for Links {

    fn into(self) -> Vec<Link> {
        self.links
    }

}

<commit_msg>lib: Add Links::{add,remove}()<commit_after>use std::convert::Into;

use libimagstore::store::Entry;

#[derive(PartialOrd, Ord, Eq, PartialEq, Clone, Debug)]
pub struct Link {
    link: String
}

impl Link {

    pub fn new(s: String) -> Link {
        Link { link: s }
    }

}

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Links {
    links: Vec<Link>,
}

impl Links {

    pub fn new(s: Vec<Link>) -> Links {
        Links { links: s }
    }

    pub fn add(&mut self, l: Link) {
        self.links.push(l);
    }

    pub fn remove(&mut self, l: Link) {
        self.links.retain(|link| l != link.clone());
    }

}

impl Into<String> for Link {

    fn into(self) -> String {
        self.link
    }

}

impl Into<Vec<Link>> for Links {

    fn into(self) -> Vec<Link> {
        self.links
    }

}

<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::io::BufRead;
use std::result::Result as RResult;

use toml::Value;
use uuid::Uuid;

use task_hookrs::task::Task as TTask;
use task_hookrs::import::{import_task, import_tasks};

use libimagstore::store::{FileLockEntry, Store};
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
use module_path::ModuleEntryPath;

use error::{TodoError, TodoErrorKind, MapErrInto};
use result::Result;

/// Task struct containing a `FileLockEntry`
#[derive(Debug)]
pub struct Task<'a>(FileLockEntry<'a>);

impl<'a> Task<'a> {

    /// Concstructs a new `Task` with a `FileLockEntry`
    pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
        Task(fle)
    }

    pub fn import<R: BufRead>(store: &'a Store, mut r: R) -> Result<(Task<'a>, Uuid)> {
        let mut line = String::new();
        r.read_line(&mut line);
        import_task(&line.as_str())
            .map_err_into(TodoErrorKind::ImportError)
            .and_then(|t| {
                let uuid = t.uuid().clone();
                t.into_task(store).map(|t| (t, uuid))
            })
    }

    /// Get a task from an import string. That is: read the imported string, get the UUID from it
    /// and try to load this UUID from store.
    ///
    /// Possible return values are:
    ///
    /// * Ok(Ok(Task))
    /// * Ok(Err(String)) - where the String is the String read from the `r` parameter
    /// * Err(_)          - where the error is an error that happened during evaluation
    ///
    pub fn get_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<RResult<Task<'a>, String>>
    {
        let mut line = String::new();
        r.read_line(&mut line);
        Task::get_from_string(store, line)
    }

    /// Get a task from a String. The String is expected to contain the JSON-representation of the
    /// Task to get from the store (only the UUID really matters in this case)
    ///
    /// For an explanation on the return values see `Task::get_from_import()`.
    pub fn get_from_string(store: &'a Store, s: String) -> Result<RResult<Task<'a>, String>> {
        import_task(s.as_str())
            .map_err_into(TodoErrorKind::ImportError)
            .map(|t| t.uuid().clone())
            .and_then(|uuid| Task::get_from_uuid(store, uuid))
            .and_then(|o| match o {
                None    => Ok(Err(s)),
                Some(t) => Ok(Ok(t)),
            })
    }

    /// Get a task from an UUID.
    ///
    /// If there is no task with this UUID, this returns `Ok(None)`.
    pub fn get_from_uuid(store: &'a Store, uuid: Uuid) -> Result<Option<Task<'a>>> {
        let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();

        store.get(store_id)
            .map(|o| o.map(Task::new))
            .map_err_into(TodoErrorKind::StoreError)
    }

    /// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to
    /// implicitely create the task if it does not exist.
    pub fn retrieve_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<Task<'a>> {
        unimplemented!()
    }

    /// Retrieve a task from a String. The String is expected to contain the JSON-representation of
    /// the Task to retrieve from the store (only the UUID really matters in this case)
    pub fn retrieve_from_string(store: &'a Store, s: String) -> Result<Task<'a>> {
        unimplemented!()
    }

    pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
        store.delete(ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid())
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all_as_ids(store: &Store) -> Result<StoreIdIterator> {
        store.retrieve_for_module("todo/taskwarrior")
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all(store: &Store) -> Result<TaskIterator> {
        Task::all_as_ids(store)
            .map(|iter| TaskIterator::new(store, iter))
    }

}

impl<'a> Deref for Task<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Task<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

/// A trait to get a `libimagtodo::task::Task` out of the implementing object.
/// This Task struct is merely a wrapper for a `FileLockEntry`, therefore the function name
/// `into_filelockentry`.
pub trait IntoTask<'a> {

    /// # Usage
    /// ```ignore
    /// use std::io::stdin;
    ///
    /// use task_hookrs::task::Task;
    /// use task_hookrs::import::import;
    /// use libimagstore::store::{Store, FileLockEntry};
    ///
    /// if let Ok(task_hookrs_task) = import(stdin()) {
    ///     // Store is given at runtime
    ///     let task = task_hookrs_task.into_filelockentry(store);
    ///     println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
    /// }
    /// ```
    fn into_task(self, store : &'a Store) -> Result<Task<'a>>;

}

impl<'a> IntoTask<'a> for TTask {

    fn into_task(self, store : &'a Store) -> Result<Task<'a>> {
        let uuid     = self.uuid();
        let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();

        match store.retrieve(store_id) {
            Err(e) => return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(mut fle) => {
                {
                    let mut header = fle.get_header_mut();
                    match header.read("todo") {
                        Ok(None) => {
                            if let Err(e) = header.set("todo", Value::Table(BTreeMap::new())) {
                                return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                            }
                        }
                        Ok(Some(_)) => { }
                        Err(e) => {
                            return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                        }
                    }

                    if let Err(e) = header.set("todo.uuid", Value::String(format!("{}",uuid))) {
                        return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                    }
                }

                // If none of the errors above have returned the function, everything is fine
                Ok(Task::new(fle))
            }
        }
    }

}

trait FromStoreId {
    fn from_storeid<'a>(&'a Store, StoreId) -> Result<Task<'a>>;
}

impl<'a> FromStoreId for Task<'a> {

    fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
        match store.retrieve(id) {
            Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(c)  => Ok(Task::new( c )),
        }
    }
}

pub struct TaskIterator<'a> {
    store: &'a Store,
    iditer: StoreIdIterator,
}

impl<'a> TaskIterator<'a> {

    pub fn new(store: &'a Store, iditer: StoreIdIterator) -> TaskIterator<'a> {
        TaskIterator {
            store: store,
            iditer: iditer,
        }
    }

}

impl<'a> Iterator for TaskIterator<'a> {
    type Item = Result<Task<'a>>;

    fn next(&mut self) -> Option<Result<Task<'a>>> {
        self.iditer.next().map(|id| Task::from_storeid(self.store, id))
    }
}

<commit_msg>Impl Task::retrieve_from_string<commit_after>use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::io::BufRead;
use std::result::Result as RResult;

use toml::Value;
use uuid::Uuid;

use task_hookrs::task::Task as TTask;
use task_hookrs::import::{import_task, import_tasks};

use libimagstore::store::{FileLockEntry, Store};
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
use module_path::ModuleEntryPath;

use error::{TodoError, TodoErrorKind, MapErrInto};
use result::Result;

/// Task struct containing a `FileLockEntry`
#[derive(Debug)]
pub struct Task<'a>(FileLockEntry<'a>);

impl<'a> Task<'a> {

    /// Concstructs a new `Task` with a `FileLockEntry`
    pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
        Task(fle)
    }

    pub fn import<R: BufRead>(store: &'a Store, mut r: R) -> Result<(Task<'a>, Uuid)> {
        let mut line = String::new();
        r.read_line(&mut line);
        import_task(&line.as_str())
            .map_err_into(TodoErrorKind::ImportError)
            .and_then(|t| {
                let uuid = t.uuid().clone();
                t.into_task(store).map(|t| (t, uuid))
            })
    }

    /// Get a task from an import string. That is: read the imported string, get the UUID from it
    /// and try to load this UUID from store.
    ///
    /// Possible return values are:
    ///
    /// * Ok(Ok(Task))
    /// * Ok(Err(String)) - where the String is the String read from the `r` parameter
    /// * Err(_)          - where the error is an error that happened during evaluation
    ///
    pub fn get_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<RResult<Task<'a>, String>>
    {
        let mut line = String::new();
        r.read_line(&mut line);
        Task::get_from_string(store, line)
    }

    /// Get a task from a String. The String is expected to contain the JSON-representation of the
    /// Task to get from the store (only the UUID really matters in this case)
    ///
    /// For an explanation on the return values see `Task::get_from_import()`.
    pub fn get_from_string(store: &'a Store, s: String) -> Result<RResult<Task<'a>, String>> {
        import_task(s.as_str())
            .map_err_into(TodoErrorKind::ImportError)
            .map(|t| t.uuid().clone())
            .and_then(|uuid| Task::get_from_uuid(store, uuid))
            .and_then(|o| match o {
                None    => Ok(Err(s)),
                Some(t) => Ok(Ok(t)),
            })
    }

    /// Get a task from an UUID.
    ///
    /// If there is no task with this UUID, this returns `Ok(None)`.
    pub fn get_from_uuid(store: &'a Store, uuid: Uuid) -> Result<Option<Task<'a>>> {
        let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();

        store.get(store_id)
            .map(|o| o.map(Task::new))
            .map_err_into(TodoErrorKind::StoreError)
    }

    /// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to
    /// implicitely create the task if it does not exist.
    pub fn retrieve_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<Task<'a>> {
        unimplemented!()
    }

    /// Retrieve a task from a String. The String is expected to contain the JSON-representation of
    /// the Task to retrieve from the store (only the UUID really matters in this case)
    pub fn retrieve_from_string(store: &'a Store, s: String) -> Result<Task<'a>> {
        Task::get_from_string(store, s)
            .and_then(|opt| match opt {
                Ok(task)    => Ok(task),
                Err(string) => import_task(string.as_str())
                    .map_err_into(TodoErrorKind::ImportError)
                    .and_then(|t| t.into_task(store)),
            })
    }

    pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
        store.delete(ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid())
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all_as_ids(store: &Store) -> Result<StoreIdIterator> {
        store.retrieve_for_module("todo/taskwarrior")
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all(store: &Store) -> Result<TaskIterator> {
        Task::all_as_ids(store)
            .map(|iter| TaskIterator::new(store, iter))
    }

}

impl<'a> Deref for Task<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Task<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

/// A trait to get a `libimagtodo::task::Task` out of the implementing object.
/// This Task struct is merely a wrapper for a `FileLockEntry`, therefore the function name
/// `into_filelockentry`.
pub trait IntoTask<'a> {

    /// # Usage
    /// ```ignore
    /// use std::io::stdin;
    ///
    /// use task_hookrs::task::Task;
    /// use task_hookrs::import::import;
    /// use libimagstore::store::{Store, FileLockEntry};
    ///
    /// if let Ok(task_hookrs_task) = import(stdin()) {
    ///     // Store is given at runtime
    ///     let task = task_hookrs_task.into_filelockentry(store);
    ///     println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
    /// }
    /// ```
    fn into_task(self, store : &'a Store) -> Result<Task<'a>>;

}

impl<'a> IntoTask<'a> for TTask {

    fn into_task(self, store : &'a Store) -> Result<Task<'a>> {
        let uuid     = self.uuid();
        let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();

        match store.retrieve(store_id) {
            Err(e) => return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(mut fle) => {
                {
                    let mut header = fle.get_header_mut();
                    match header.read("todo") {
                        Ok(None) => {
                            if let Err(e) = header.set("todo", Value::Table(BTreeMap::new())) {
                                return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                            }
                        }
                        Ok(Some(_)) => { }
                        Err(e) => {
                            return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                        }
                    }

                    if let Err(e) = header.set("todo.uuid", Value::String(format!("{}",uuid))) {
                        return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                    }
                }

                // If none of the errors above have returned the function, everything is fine
                Ok(Task::new(fle))
            }
        }
    }

}

trait FromStoreId {
    fn from_storeid<'a>(&'a Store, StoreId) -> Result<Task<'a>>;
}

impl<'a> FromStoreId for Task<'a> {

    fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
        match store.retrieve(id) {
            Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(c)  => Ok(Task::new( c )),
        }
    }
}

pub struct TaskIterator<'a> {
    store: &'a Store,
    iditer: StoreIdIterator,
}

impl<'a> TaskIterator<'a> {

    pub fn new(store: &'a Store, iditer: StoreIdIterator) -> TaskIterator<'a> {
        TaskIterator {
            store: store,
            iditer: iditer,
        }
    }

}

impl<'a> Iterator for TaskIterator<'a> {
    type Item = Result<Task<'a>>;

    fn next(&mut self) -> Option<Result<Task<'a>>> {
        self.iditer.next().map(|id| Task::from_storeid(self.store, id))
    }
}

<|endoftext|>