<commit_before>//! Custom error types.

use redis::RedisError;
use r2d2::InitializationError;
use std::io;
use std::borrow::Cow;

/// A ``SpaceapiServerError`` wraps general problems that can occur in the SpaceAPI server.
quick_error! {
    #[derive(Debug)]
    pub enum SpaceapiServerError {
        Redis(err: RedisError) {
            from()
            cause(err)
        }
        R2d2(err: InitializationError) {
            from()
            cause(err)
        }
        IoError(err: io::Error) {
            from()
            cause(err)
        }
        Message(err: Cow<'static, str>) {
            description(&**err)
            from(s: &'static str) -> (s.into())
            from(s: String) -> (s.into())
        }
    }
}
<commit_msg>Fix warning about unused_doc_comments (#91)<commit_after>//! Custom error types.

use r2d2::InitializationError;
use redis::RedisError;
use std::borrow::Cow;
use std::io;

quick_error! {
    /// A ``SpaceapiServerError`` wraps general problems that can occur in the SpaceAPI server.
    #[derive(Debug)]
    pub enum SpaceapiServerError {
        Redis(err: RedisError) {
            from()
            cause(err)
        }
        R2d2(err: InitializationError) {
            from()
            cause(err)
        }
        IoError(err: io::Error) {
            from()
            cause(err)
        }
        Message(err: Cow<'static, str>) {
            description(&**err)
            from(s: &'static str) -> (s.into())
            from(s: String) -> (s.into())
        }
    }
}
<|endoftext|>


<commit_before>
use std::io::Read;  // Trait providing read_to_string()

use hyper;
use rustc_serialize;

use projects;
use groups;

pub const API_VERSION: u16 = 3;

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct Version {
    pub version: String,
    pub revision: String,
}

#[derive(Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

#[derive(Debug)]
pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
    pagination: Pagination,
}

impl GitLab {

    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port:   port,
            private_token: private_token.to_string(),
            client: hyper::Client::new(),
            pagination: Pagination {page: 1, per_page: 20},
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", &domain, 80, &private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", &domain, 443, &private_token)
    }

    pub fn build_url(&self, command: &str) -> String {
        format!("{}://{}:{}/api/v{}/{}?private_token={}&page={}&per_page={}",
                                self.scheme,
                                self.domain,
                                self.port,
                                API_VERSION,
                                command,
                                self.private_token,
                                self.pagination.page,
                                self.pagination.per_page)
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        // self.get("version")
        let url = self.build_url("version");
        // Close connections after each GET.
        let res = self.client.get(&url).header(hyper::header::Connection::close()).send();

        res
    }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = pagination;
    }

    pub fn get<T>(&self, command: &str) -> Result<T, rustc_serialize::json::DecoderError>
            where T: rustc_serialize::Decodable {

        let url = self.build_url(command);
        let mut res: hyper::client::Response =
                        self.client
                        .get(&url)
                        .header(hyper::header::Connection::close())
                        .send()
                        .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        rustc_serialize::json::decode(body.as_str())
    }

    pub fn version(&self) -> Result<Version, rustc_serialize::json::DecoderError> {
        self.get("version")
    }

    pub fn groups(&self) -> Result<groups::Groups, rustc_serialize::json::DecoderError> {
        self.get("groups")
    }

    pub fn projects(&self) -> Result<projects::Projects, rustc_serialize::json::DecoderError> {
        self.get("groups")
    }
}

#[cfg(test)]
mod tests {
    use std::env;
    use gitlab::GitLab;
    use gitlab::Pagination;

    #[test]
    fn list_groups() {
        let token = match env::var("GITLAB_TOKEN") {
            Ok(val) => val,
            Err(_)  => panic!("Please set environment variable 'GITLAB_TOKEN'"),
        };

        let mut gl = GitLab::new_https("gitlab.com", &token);
        gl.set_pagination(Pagination{page: 1, per_page: 100});
        println!("gl: {:?}", gl);

        let groups = gl.groups().unwrap();
        println!("groups: {:?}", groups);
    }

    #[test]
    fn list_projects() {
        let token = match env::var("GITLAB_TOKEN") {
            Ok(val) => val,
            Err(_)  => panic!("Please set environment variable 'GITLAB_TOKEN'"),
        };

        let mut gl = GitLab::new_https("gitlab.com", &token);
        gl.set_pagination(Pagination{page: 1, per_page: 100});
        println!("gl: {:?}", gl);

        println!("projects: {:?}", gl.projects().unwrap());
        // assert_eq!(gl.attempt_connection().unwrap().status, hyper::Ok);

    }
}
<commit_msg>Allow going through an Http proxy by setting HTTP_PROXY environment variable<commit_after>
use std::io::Read;  // Trait providing read_to_string()

use std::env;

use hyper;
use rustc_serialize;

use projects;
use groups;

pub const API_VERSION: u16 = 3;

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct Version {
    pub version: String,
    pub revision: String,
}

#[derive(Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

#[derive(Debug)]
pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
    pagination: Pagination,
}

impl GitLab {

    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port:   port,
            private_token: private_token.to_string(),
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    println!("proxy: {:?}", proxy);
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                },
                Err(_) => hyper::Client::new(),
            },
            pagination: Pagination {page: 1, per_page: 20},
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", &domain, 80, &private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", &domain, 443, &private_token)
    }

    pub fn build_url(&self, command: &str) -> String {
        format!("{}://{}:{}/api/v{}/{}?private_token={}&page={}&per_page={}",
                                self.scheme,
                                self.domain,
                                self.port,
                                API_VERSION,
                                command,
                                self.private_token,
                                self.pagination.page,
                                self.pagination.per_page)
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        // self.get("version")
        let url = self.build_url("version");
        // Close connections after each GET.
        let res = self.client.get(&url).header(hyper::header::Connection::close()).send();

        res
    }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = pagination;
    }

    pub fn get<T>(&self, command: &str) -> Result<T, rustc_serialize::json::DecoderError>
            where T: rustc_serialize::Decodable {

        let url = self.build_url(command);
        let mut res: hyper::client::Response =
                        self.client
                        .get(&url)
                        .header(hyper::header::Connection::close())
                        .send()
                        .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        rustc_serialize::json::decode(body.as_str())
    }

    pub fn version(&self) -> Result<Version, rustc_serialize::json::DecoderError> {
        self.get("version")
    }

    pub fn groups(&self) -> Result<groups::Groups, rustc_serialize::json::DecoderError> {
        self.get("groups")
    }

    pub fn projects(&self) -> Result<projects::Projects, rustc_serialize::json::DecoderError> {
        self.get("groups")
    }
}

#[cfg(test)]
mod tests {
    use std::env;
    use gitlab::GitLab;
    use gitlab::Pagination;

    #[test]
    fn list_groups() {
        let token = match env::var("GITLAB_TOKEN") {
            Ok(val) => val,
            Err(_)  => panic!("Please set environment variable 'GITLAB_TOKEN'"),
        };

        let mut gl = GitLab::new_https("gitlab.com", &token);
        gl.set_pagination(Pagination{page: 1, per_page: 100});
        println!("gl: {:?}", gl);

        let groups = gl.groups().unwrap();
        println!("groups: {:?}", groups);
    }

    #[test]
    fn list_projects() {
        let token = match env::var("GITLAB_TOKEN") {
            Ok(val) => val,
            Err(_)  => panic!("Please set environment variable 'GITLAB_TOKEN'"),
        };

        let mut gl = GitLab::new_https("gitlab.com", &token);
        gl.set_pagination(Pagination{page: 1, per_page: 100});
        println!("gl: {:?}", gl);

        println!("projects: {:?}", gl.projects().unwrap());
        // assert_eq!(gl.attempt_connection().unwrap().status, hyper::Ok);

    }
}
<|endoftext|>


<commit_before>//! This module contains a (reasonably) optimized implementation of the greedy algorithm, which is
//! also generic over any implementor of the `Objective` trait.
//!
//! The greedy algorithm is exceptionally simple in concept: to construct a solution `S` with `k`
//! elements maximizing `f`, iteratively select the element `e` that maximizes `Δ(e, S) =
//! f(S ∪ {e}) - f(S)` and add that to the solution.
//!
//! The performance of this algorithm depends *heavily* on the implementation of the `Objective`.
//! In particular, `depends` should be as fast as possible (preferably no more than a copy), and
//! the dependent sets should be made as small as possible while preserving correctness.
//!
//! Further, any `Objective` to be optimized with the greedy algorithm should override the `delta`
//! method to improve performance, as even when the `benefit` function is trivial the default
//! implementation still includes an avoidable copy.
use std::collections::BinaryHeap;
use std::cmp::Ordering;
use slog::{Drain, Logger};
use slog_stdlog::StdLog;
use objective::*;
use errors::*;

struct WeightedNode<O: Objective> {
    pub weight: f64,
    pub node: O::Element,
}

impl<O: Objective> PartialEq for WeightedNode<O> {
    fn eq(&self, other: &Self) -> bool {
        self.weight == other.weight
    }
}

impl<O: Objective> Eq for WeightedNode<O> {}

impl<O: Objective> PartialOrd for WeightedNode<O> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.weight.partial_cmp(&other.weight)
    }
}

impl<O: Objective> Ord for WeightedNode<O> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight.partial_cmp(&other.weight).unwrap()
    }
}

/// A max-heap-based state-tracking generic implementation of the greedy algorithm finding a
/// set `S` with `k` elements that approximately maximizes the objective `f` (represented by
/// `obj`). If the primal curvature (`obj.nabla`) can be bounded above by 1 and the set of valid
/// solutions is equal `{S | |S| ≤ k, S ⊆ X}` (where `X` is all elements), then this solution is
/// at least `(1 - 1/e) ≈ 63%` as good as any optimal solution.
///
/// The performance of this algorithm depends *heavily* on the implementation of the `Objective`.
/// In particular, `depends` should be as fast as possible (preferably no more than a copy), and
/// the dependent sets should be made as small as possible while preserving correctness.
///
/// Further, any `Objective` to be optimized with the greedy algorithm should override the `delta`
/// method to improve performance, as even when the `benefit` function is trivial the default
/// implementation still includes an avoidable copy.
pub fn greedy<O: Objective + Sync>(obj: &O,
                                   k: usize,
                                   log: Option<Logger>)
                                   -> Result<(f64, Vec<O::Element>, O::State)>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let log = log.unwrap_or_else(|| Logger::root(StdLog.fuse(), o!()));
    let mut state = O::State::default();
    let mut solset = Set::default();
    fn reheap<O: Objective>(sol: &Set<O::Element>,
                            obj: &O,
                            state: &O::State,
                            prior: BinaryHeap<WeightedNode<O>>,
                            elements: Set<O::Element>)
                            -> Result<BinaryHeap<WeightedNode<O>>> {
        prior.into_iter()
            .filter(|ref we| !elements.contains(&we.node))
            .map(|we| Ok(we))
            .chain(elements.iter().cloned().map(|e| {
                let w = obj.delta(e, &sol, &state)?;
                Ok(WeightedNode {
                    node: e,
                    weight: w,
                })
            }))
            .collect()
    }

    let elements = obj.elements().collect::<Set<_>>();
    debug!(log, "initializing heap");
    let mut heap = reheap(&solset,
                          obj,
                          &state,
                          BinaryHeap::with_capacity(elements.len()),
                          elements)?;
    debug!(log, "done initializing heap");

    let mut f = 0.0;
    let mut sol = Vec::with_capacity(k);
    let mut i = 0;
    while let Some(top) = heap.pop() {
        sol.push(top.node);
        solset.insert(top.node.into());
        f += top.weight;
        trace!(log, "selected element"; "node" => format!("{:?}", top.node), "objective value" => f);
        obj.insert_mut(top.node, &mut state)?;
        // NOTE: this assumes a symmetric dependency relationship,
        // i.e. v ∈ D(u) ⇐⇒ u ∈ D(v) ∀ u,v ∈ E
        heap = reheap(&solset,
                      obj,
                      &state,
                      heap,
                      obj.depends(top.node, &state)?
                          .filter(|&u| !solset.contains(&u))
                          .collect())?;

        // can't && i < k in a while let, unfortunately
        i += 1;
        if i >= k {
            break;
        }
    }

    debug!(log, "greedy solution"; "f" => f);
    Ok((f, sol, state))
}

/// A lazy variant of `greedy` with no additional (implementation) requirements. However, the objective must be
/// submodular to use this.
///
/// It operates by recording elements that have been added to the solution, but not yet been
/// `insert()`-ed. These are termed "incomplete insertions". As long as the top element is
/// independent of every incompletely inserted element, it is possible to continue delaying the
/// insert call.
///
/// The benefit of using this method over `greedy` depends highly on the structure of dependencies:
/// if high-value elements are dependent primarily on low-value elements, then this will eliminate
/// most insertions. On the other hand, if the highest-value elements have strong dependencies,
/// this will eliminate nearly none of the insertions.
pub fn lazy_greedy<O: Objective + curvature::Submodular>
    (obj: &O,
     k: usize,
     log: Option<Logger>)
     -> Result<(f64, Vec<O::Element>, O::State)> {
    let log = log.unwrap_or_else(|| Logger::root(StdLog.fuse(), o!()));
    let mut state = O::State::default();
    let mut solset = Set::default();
    // an insertion is *incomplete* if it has been added to the solution, but it `insert` has not
    // been called for it to update the state.
    let mut incomplete_insertions: Set<O::Element> = Set::default();
    let mut time_since_reheap = 0;
    let mut times = Vec::with_capacity(k);
    let mut deps_size = Vec::new();
    fn reheap<O: Objective>(sol: &Set<O::Element>,
                            obj: &O,
                            state: &O::State,
                            prior: BinaryHeap<WeightedNode<O>>,
                            elements: Set<O::Element>)
                            -> Result<BinaryHeap<WeightedNode<O>>> {
        prior.into_iter()
            .filter(|ref we| !elements.contains(&we.node))
            .map(|we| Ok(we))
            .chain(elements.iter().cloned().map(|e| {
                let w = obj.delta(e, &sol, &state)?;
                Ok(WeightedNode {
                    node: e,
                    weight: w,
                })
            }))
            .collect()
    }

    let elements = obj.elements().collect::<Set<_>>();
    debug!(log, "initializing heap");
    let mut heap = reheap(&solset,
                          obj,
                          &state,
                          BinaryHeap::with_capacity(elements.len()),
                          elements)?;
    debug!(log, "done initializing heap");

    let mut f = 0.0;
    let mut sol = Vec::with_capacity(k);
    let mut i = 0;
    while let Some(top) = heap.pop() {
        let deps = obj.depends(top.node, &state)?.collect::<Set<_>>();
        let incomplete_deps = deps.iter()
            .filter(|&u| incomplete_insertions.contains(&u))
            .cloned()
            .collect::<Set<_>>();
        let mut todos = Vec::new();
        for &dep in &incomplete_deps {
            obj.insert_mut(dep, &mut state)?;
            incomplete_insertions.remove(&dep);
            todos.extend(obj.depends(dep, &state)?);
        }

        if incomplete_deps.len() > 0 {
            // this may no longer be the top node, put it back on the heap and reheap
            heap.push(top);
            heap = reheap(&solset, obj, &state, heap, todos.into_iter().collect())?;
            deps_size.push(incomplete_deps.len());
            times.push(time_since_reheap);
            time_since_reheap = 0;
            continue;
        }

        time_since_reheap += 1;

        sol.push(top.node);
        solset.insert(top.node.into());
        incomplete_insertions.insert(top.node);
        f += top.weight;
        trace!(log, "selected element"; "node" => format!("{:?}", top.node), "objective value" => f);

        // can't && i < k in a while let, unfortunately
        i += 1;
        if i >= k {
            break;
        }
    }

    info!(log, "greedy solution";
          "f" => f,
          "lazy speedup" => incomplete_insertions.len() as f64 / k as f64,
          "insertions skipped" => incomplete_insertions.len(),
          "avg steps between reheaps" => times.iter().sum::<usize>() as f64 / times.len() as f64,
          "avg incomplete deps at reheap" => deps_size.iter().sum::<usize>() as f64 / deps_size.len() as f64);
    Ok((f, sol, state))
}

/// An even lazier lazy greedy solver than `lazy_greedy`. This has both the submodularity
/// requirement of `lazy_greedy` and additional implementation requirements (the objective must
/// also implement `LazyObjective`), but is significantly more efficient in practice.
///
/// The difference between this and `lazy_greedy` is that instead of merely delaying insertions,
/// this replaces them with *re-evaluations* of the marginal gain. If this can be implemented at
/// least as efficiently as the above, there is virtually no cost to this.
///
/// One of the big wins of this method is the ability to avoid (expensive) reheap operations
/// because only a single element is every reheaped at once, so `pop`/`insert` are used instead of
/// reprocessing the heap.
///
/// **Note:** The state from `lazier_greedy` is not necessarily compatible with the states for
/// non-lazy functions. Use caution when passing the resulting `State` struct to those functions.
///
/// # Panics
/// Panics if the objective is not submodular.
pub fn lazier_greedy<O: Objective + LazyObjective + curvature::Submodular>
    (obj: &O,
     k: usize,
     log: Option<Logger>)
     -> Result<(f64, Vec<O::Element>, O::State)> {
    let log = log.unwrap_or_else(|| Logger::root(StdLog.fuse(), o!()));
    let mut state = O::State::default();
    let mut solset = Set::default();
    // an insertion is *incomplete* if it has been added to the solution, but it `insert` has not
    // been called for it to update the state.
    fn reheap<O: Objective>(sol: &Set<O::Element>,
                            obj: &O,
                            state: &O::State,
                            prior: BinaryHeap<WeightedNode<O>>,
                            elements: Set<O::Element>)
                            -> Result<BinaryHeap<WeightedNode<O>>> {
        prior.into_iter()
            .filter(|ref we| !elements.contains(&we.node))
            .map(|we| Ok(we))
            .chain(elements.iter().cloned().map(|e| {
                let w = obj.delta(e, &sol, &state)?;
                Ok(WeightedNode {
                    node: e,
                    weight: w,
                })
            }))
            .collect()
    }

    let elements = obj.elements().collect::<Set<_>>();
    debug!(log, "initializing heap");
    let mut heap = reheap(&solset,
                          obj,
                          &state,
                          BinaryHeap::with_capacity(elements.len()),
                          elements)?;
    debug!(log, "done initializing heap");

    let mut f = 0.0;
    let mut sol = Vec::with_capacity(k);
    let mut i = 0;
    while let Some(top) = heap.pop() {
        let updated_gain = if top.weight == 0.0 {
            // no point in updating gain
            Some(top.weight)
        } else {
            obj.update_lazy_mut(top.node, &solset, &mut state)?
        };

        match updated_gain {
            None => {}
            Some(gain) => {
                if !heap.is_empty() && gain < heap.peek().unwrap().weight {
                    // updating the marginal gain caused the order of this element to be moved around
                    heap.push(WeightedNode {
                        node: top.node,
                        weight: gain,
                    });
                    continue;
                }
            }
        };

        // if we got this far, we know that we have the real top
        sol.push(top.node);
        solset.insert(top.node.into());
        obj.insert_lazy_mut(top.node, &mut state)?;
        f += updated_gain.unwrap_or(top.weight);
        // debug!(log, "benefit"; "f" => f, "actual" => obj.benefit(&solset, &state)?);
        trace!(log, "selected element"; "node" => format!("{:?}", top.node), "objective value" => f);

        // can't && i < k in a while let, unfortunately
        i += 1;
        if i >= k {
            break;
        }
    }

    info!(log, "greedy solution";
          "f" => f);
    Ok((f, sol, state))
}
<commit_msg>Added constrained lazier_greedy.<commit_after>//! This module contains a (reasonably) optimized implementation of the greedy algorithm, which is
//! also generic over any implementor of the `Objective` trait.
//!
//! The greedy algorithm is exceptionally simple in concept: to construct a solution `S` with `k`
//! elements maximizing `f`, iteratively select the element `e` that maximizes `Δ(e, S) =
//! f(S ∪ {e}) - f(S)` and add that to the solution.
//!
//! The performance of this algorithm depends *heavily* on the implementation of the `Objective`.
//! In particular, `depends` should be as fast as possible (preferably no more than a copy), and
//! the dependent sets should be made as small as possible while preserving correctness.
//!
//! Further, any `Objective` to be optimized with the greedy algorithm should override the `delta`
//! method to improve performance, as even when the `benefit` function is trivial the default
//! implementation still includes an avoidable copy.
use std::collections::BinaryHeap;
use std::cmp::Ordering;
use slog::{Drain, Logger};
use slog_stdlog::StdLog;
use objective::*;
use errors::*;

struct WeightedNode<O: Objective> {
    pub weight: f64,
    pub node: O::Element,
}

impl<O: Objective> PartialEq for WeightedNode<O> {
    fn eq(&self, other: &Self) -> bool {
        self.weight == other.weight
    }
}

impl<O: Objective> Eq for WeightedNode<O> {}

impl<O: Objective> PartialOrd for WeightedNode<O> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.weight.partial_cmp(&other.weight)
    }
}

impl<O: Objective> Ord for WeightedNode<O> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight.partial_cmp(&other.weight).unwrap()
    }
}

/// A max-heap-based state-tracking generic implementation of the greedy algorithm finding a
/// set `S` with `k` elements that approximately maximizes the objective `f` (represented by
/// `obj`). If the primal curvature (`obj.nabla`) can be bounded above by 1 and the set of valid
/// solutions is equal `{S | |S| ≤ k, S ⊆ X}` (where `X` is all elements), then this solution is
/// at least `(1 - 1/e) ≈ 63%` as good as any optimal solution.
///
/// The performance of this algorithm depends *heavily* on the implementation of the `Objective`.
/// In particular, `depends` should be as fast as possible (preferably no more than a copy), and
/// the dependent sets should be made as small as possible while preserving correctness.
///
/// Further, any `Objective` to be optimized with the greedy algorithm should override the `delta`
/// method to improve performance, as even when the `benefit` function is trivial the default
/// implementation still includes an avoidable copy.
pub fn greedy<O: Objective + Sync>(obj: &O,
                                   k: usize,
                                   log: Option<Logger>)
                                   -> Result<(f64, Vec<O::Element>, O::State)>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let log = log.unwrap_or_else(|| Logger::root(StdLog.fuse(), o!()));
    let mut state = O::State::default();
    let mut solset = Set::default();
    fn reheap<O: Objective>(sol: &Set<O::Element>,
                            obj: &O,
                            state: &O::State,
                            prior: BinaryHeap<WeightedNode<O>>,
                            elements: Set<O::Element>)
                            -> Result<BinaryHeap<WeightedNode<O>>> {
        prior.into_iter()
            .filter(|ref we| !elements.contains(&we.node))
            .map(|we| Ok(we))
            .chain(elements.iter().cloned().map(|e| {
                let w = obj.delta(e, &sol, &state)?;
                Ok(WeightedNode {
                    node: e,
                    weight: w,
                })
            }))
            .collect()
    }

    let elements = obj.elements().collect::<Set<_>>();
    debug!(log, "initializing heap");
    let mut heap = reheap(&solset,
                          obj,
                          &state,
                          BinaryHeap::with_capacity(elements.len()),
                          elements)?;
    debug!(log, "done initializing heap");

    let mut f = 0.0;
    let mut sol = Vec::with_capacity(k);
    let mut i = 0;
    while let Some(top) = heap.pop() {
        sol.push(top.node);
        solset.insert(top.node.into());
        f += top.weight;
        trace!(log, "selected element"; "node" => format!("{:?}", top.node), "objective value" => f);
        obj.insert_mut(top.node, &mut state)?;
        // NOTE: this assumes a symmetric dependency relationship,
        // i.e. v ∈ D(u) ⇐⇒ u ∈ D(v) ∀ u,v ∈ E
        heap = reheap(&solset,
                      obj,
                      &state,
                      heap,
                      obj.depends(top.node, &state)?
                          .filter(|&u| !solset.contains(&u))
                          .collect())?;

        // can't && i < k in a while let, unfortunately
        i += 1;
        if i >= k {
            break;
        }
    }

    debug!(log, "greedy solution"; "f" => f);
    Ok((f, sol, state))
}

/// A lazy variant of `greedy` with no additional (implementation) requirements. However, the objective must be
/// submodular to use this.
///
/// It operates by recording elements that have been added to the solution, but not yet been
/// `insert()`-ed. These are termed "incomplete insertions". As long as the top element is
/// independent of every incompletely inserted element, it is possible to continue delaying the
/// insert call.
///
/// The benefit of using this method over `greedy` depends highly on the structure of dependencies:
/// if high-value elements are dependent primarily on low-value elements, then this will eliminate
/// most insertions. On the other hand, if the highest-value elements have strong dependencies,
/// this will eliminate nearly none of the insertions.
pub fn lazy_greedy<O: Objective + curvature::Submodular>
    (obj: &O,
     k: usize,
     log: Option<Logger>)
     -> Result<(f64, Vec<O::Element>, O::State)> {
    let log = log.unwrap_or_else(|| Logger::root(StdLog.fuse(), o!()));
    let mut state = O::State::default();
    let mut solset = Set::default();
    // an insertion is *incomplete* if it has been added to the solution, but it `insert` has not
    // been called for it to update the state.
    let mut incomplete_insertions: Set<O::Element> = Set::default();
    let mut time_since_reheap = 0;
    let mut times = Vec::with_capacity(k);
    let mut deps_size = Vec::new();
    fn reheap<O: Objective>(sol: &Set<O::Element>,
                            obj: &O,
                            state: &O::State,
                            prior: BinaryHeap<WeightedNode<O>>,
                            elements: Set<O::Element>)
                            -> Result<BinaryHeap<WeightedNode<O>>> {
        prior.into_iter()
            .filter(|ref we| !elements.contains(&we.node))
            .map(|we| Ok(we))
            .chain(elements.iter().cloned().map(|e| {
                let w = obj.delta(e, &sol, &state)?;
                Ok(WeightedNode {
                    node: e,
                    weight: w,
                })
            }))
            .collect()
    }

    let elements = obj.elements().collect::<Set<_>>();
    debug!(log, "initializing heap");
    let mut heap = reheap(&solset,
                          obj,
                          &state,
                          BinaryHeap::with_capacity(elements.len()),
                          elements)?;
    debug!(log, "done initializing heap");

    let mut f = 0.0;
    let mut sol = Vec::with_capacity(k);
    let mut i = 0;
    while let Some(top) = heap.pop() {
        let deps = obj.depends(top.node, &state)?.collect::<Set<_>>();
        let incomplete_deps = deps.iter()
            .filter(|&u| incomplete_insertions.contains(&u))
            .cloned()
            .collect::<Set<_>>();
        let mut todos = Vec::new();
        for &dep in &incomplete_deps {
            obj.insert_mut(dep, &mut state)?;
            incomplete_insertions.remove(&dep);
            todos.extend(obj.depends(dep, &state)?);
        }

        if incomplete_deps.len() > 0 {
            // this may no longer be the top node, put it back on the heap and reheap
            heap.push(top);
            heap = reheap(&solset, obj, &state, heap, todos.into_iter().collect())?;
            deps_size.push(incomplete_deps.len());
            times.push(time_since_reheap);
            time_since_reheap = 0;
            continue;
        }

        time_since_reheap += 1;

        sol.push(top.node);
        solset.insert(top.node.into());
        incomplete_insertions.insert(top.node);
        f += top.weight;
        trace!(log, "selected element"; "node" => format!("{:?}", top.node), "objective value" => f);

        // can't && i < k in a while let, unfortunately
        i += 1;
        if i >= k {
            break;
        }
    }

    info!(log, "greedy solution";
          "f" => f,
          "lazy speedup" => incomplete_insertions.len() as f64 / k as f64,
          "insertions skipped" => incomplete_insertions.len(),
          "avg steps between reheaps" => times.iter().sum::<usize>() as f64 / times.len() as f64,
          "avg incomplete deps at reheap" => deps_size.iter().sum::<usize>() as f64 / deps_size.len() as f64);
    Ok((f, sol, state))
}

/// An even lazier lazy greedy solver than `lazy_greedy`. This has both the submodularity
/// requirement of `lazy_greedy` and additional implementation requirements (the objective must
/// also implement `LazyObjective`), but is significantly more efficient in practice.
///
/// The difference between this and `lazy_greedy` is that instead of merely delaying insertions,
/// this replaces them with *re-evaluations* of the marginal gain. If this can be implemented at
/// least as efficiently as the above, there is virtually no cost to this.
///
/// One of the big wins of this method is the ability to avoid (expensive) reheap operations
/// because only a single element is every reheaped at once, so `pop`/`insert` are used instead of
/// reprocessing the heap.
///
/// **Note:** The state from `lazier_greedy` is not necessarily compatible with the states for
/// non-lazy functions. Use caution when passing the resulting `State` struct to those functions.
///
/// # Panics
/// Panics if the objective is not submodular.
pub fn lazier_greedy<O: Objective + LazyObjective + curvature::Submodular>
    (obj: &O,
     k: usize,
     log: Option<Logger>)
     -> Result<(f64, Vec<O::Element>, O::State)> {
    let log = log.unwrap_or_else(|| Logger::root(StdLog.fuse(), o!()));
    let mut state = O::State::default();
    let mut solset = Set::default();
    // an insertion is *incomplete* if it has been added to the solution, but it `insert` has not
    // been called for it to update the state.
    fn reheap<O: Objective>(sol: &Set<O::Element>,
                            obj: &O,
                            state: &O::State,
                            prior: BinaryHeap<WeightedNode<O>>,
                            elements: Set<O::Element>)
                            -> Result<BinaryHeap<WeightedNode<O>>> {
        prior.into_iter()
            .filter(|ref we| !elements.contains(&we.node))
            .map(|we| Ok(we))
            .chain(elements.iter().cloned().map(|e| {
                let w = obj.delta(e, &sol, &state)?;
                Ok(WeightedNode {
                    node: e,
                    weight: w,
                })
            }))
            .collect()
    }

    let elements = obj.elements().collect::<Set<_>>();
    debug!(log, "initializing heap");
    let mut heap = reheap(&solset,
                          obj,
                          &state,
                          BinaryHeap::with_capacity(elements.len()),
                          elements)?;
    debug!(log, "done initializing heap");

    let mut f = 0.0;
    let mut sol = Vec::with_capacity(k);
    let mut i = 0;
    while let Some(top) = heap.pop() {
        let updated_gain = if top.weight == 0.0 {
            // no point in updating gain
            Some(top.weight)
        } else {
            obj.update_lazy_mut(top.node, &solset, &mut state)?
        };

        match updated_gain {
            None => {}
            Some(gain) => {
                if !heap.is_empty() && gain < heap.peek().unwrap().weight {
                    // updating the marginal gain caused the order of this element to be moved around
                    heap.push(WeightedNode {
                        node: top.node,
                        weight: gain,
                    });
                    continue;
                }
            }
        };

        // if we got this far, we know that we have the real top
        sol.push(top.node);
        solset.insert(top.node.into());
        obj.insert_lazy_mut(top.node, &mut state)?;
        f += updated_gain.unwrap_or(top.weight);
        // debug!(log, "benefit"; "f" => f, "actual" => obj.benefit(&solset, &state)?);
        trace!(log, "selected element"; "node" => format!("{:?}", top.node), "objective value" => f);

        // can't && i < k in a while let, unfortunately
        i += 1;
        if i >= k {
            break;
        }
    }

    info!(log, "greedy solution";
          "f" => f);
    Ok((f, sol, state))
}

/// Constrained version of `lazier_greedy`
///
/// The `constraint` function `C(S, x) -> bool` should return `true` if adding `x` to `S` would
/// produce a solution that satisfies the constraints. It is assumed that if `C(S, x) = false`,
/// then there is no `S' ⊇ S` for which `C(S', x) = true`. This condition holds for matroids and
/// independence systems, which cover the vast majority of constraint types we are interested in.
pub fn lazier_greedy_constrained<O: Objective + LazyObjective + curvature::Submodular,
                                 F: Fn(&[O::Element], O::Element) -> bool>
    (obj: &O,
     k: usize,
     constraint: F,
     log: Option<Logger>)
     -> Result<(f64, Vec<O::Element>, O::State)> {
    let log = log.unwrap_or_else(|| Logger::root(StdLog.fuse(), o!()));
    let mut state = O::State::default();
    let mut solset = Set::default();
    // an insertion is *incomplete* if it has been added to the solution, but it `insert` has not
    // been called for it to update the state.
    fn reheap<O: Objective>(sol: &Set<O::Element>,
                            obj: &O,
                            state: &O::State,
                            prior: BinaryHeap<WeightedNode<O>>,
                            elements: Set<O::Element>)
                            -> Result<BinaryHeap<WeightedNode<O>>> {
        prior.into_iter()
            .filter(|ref we| !elements.contains(&we.node))
            .map(|we| Ok(we))
            .chain(elements.iter().cloned().map(|e| {
                let w = obj.delta(e, &sol, &state)?;
                Ok(WeightedNode {
                    node: e,
                    weight: w,
                })
            }))
            .collect()
    }

    let elements = obj.elements().collect::<Set<_>>();
    debug!(log, "initializing heap");
    let mut heap = reheap(&solset,
                          obj,
                          &state,
                          BinaryHeap::with_capacity(elements.len()),
                          elements)?;
    debug!(log, "done initializing heap");

    let mut f = 0.0;
    let mut sol = Vec::with_capacity(k);
    let mut i = 0;
    while let Some(top) = heap.pop() {
        if !constraint(&sol, top.node) {
            // adding this to the solution violates the constraints, continue onward
            // since this is simple greedy, we know that we will never be able add this element to
            // the solution.
            continue;
        }
        let updated_gain = if top.weight == 0.0 {
            // no point in updating gain
            Some(top.weight)
        } else {
            obj.update_lazy_mut(top.node, &solset, &mut state)?
        };

        match updated_gain {
            None => {}
            Some(gain) => {
                if !heap.is_empty() && gain < heap.peek().unwrap().weight {
                    // updating the marginal gain caused the order of this element to be moved around
                    heap.push(WeightedNode {
                        node: top.node,
                        weight: gain,
                    });
                    continue;
                }
            }
        };

        // if we got this far, we know that we have the real top
        sol.push(top.node);
        solset.insert(top.node.into());
        obj.insert_lazy_mut(top.node, &mut state)?;
        f += updated_gain.unwrap_or(top.weight);
        // debug!(log, "benefit"; "f" => f, "actual" => obj.benefit(&solset, &state)?);
        trace!(log, "selected element"; "node" => format!("{:?}", top.node), "objective value" => f);

        // can't && i < k in a while let, unfortunately
        i += 1;
        if i >= k {
            break;
        }
    }

    info!(log, "greedy solution";
          "f" => f);
    Ok((f, sol, state))
}
<|endoftext|>


<commit_before>use std::sync::{Mutex, Arc};
use std::result::Result;

use serde_json::Value;
use gossyp_base::environment::Environment;

use super::binding_environment::*;
use super::script_interpreter::*;
use super::bind_statement::*;
use super::evaluate_statement::*;
use super::script::*;
use super::bound_script::*;

///
/// Represents a tool that can be used to evaluate scripts and maintains
/// state (useful for evaluation in a REPL environment)
///
#[derive(Clone)]
pub struct StatefulEvalTool {
    binding:    Arc<Mutex<Box<BindingEnvironment>>>,
    execution:  Arc<Mutex<ScriptExecutionEnvironment>>
}

impl StatefulEvalTool {
    pub fn new() -> StatefulEvalTool {
        StatefulEvalTool {
            binding: Arc::new(Mutex::new(BindingEnvironment::new())),
            execution: Arc::new(Mutex::new(ScriptExecutionEnvironment::new()))
        }
    }

    ///
    /// Evaluates an unbound statement using this tool
    ///
    pub fn evaluate_unbound_statement(&self, script: &Script, environment: &Environment) -> Result<Value, Value> {
        self.evaluate_statement(&self.bind_statement(script, environment)?, environment)
    }

    ///
    /// Binds a statement to this tool
    ///
    pub fn bind_statement(&self, script: &Script, environment: &Environment) -> Result<BoundScript, Value> {
        // Merge the stuff in the external environment with the stored environment
        let our_environment             = &mut **self.binding.lock().unwrap();
        let their_environment           = BindingEnvironment::from_environment(environment);
        let mut combined_environment    = BindingEnvironment::combine(our_environment, &*their_environment);

        // Bind to the combined environments
        bind_statement(script, &mut *combined_environment)
    }

    ///
    /// Evaluates a statement in the environment represented by this tool
    ///
    pub fn evaluate_statement(&self, script: &BoundScript, environment: &Environment) -> Result<Value, Value> {
        evaluate_statement(script, environment, &mut *self.execution.lock().unwrap())
    }
}

#[cfg(test)]
mod test {
    use gossyp_base::basic::*;
    use super::*;
    use serde_json::*;

    #[test]
    fn can_bind_variable_using_stateful_tool() {
        let eval    = StatefulEvalTool::new();
        let env     = EmptyEnvironment::new();

        // var x = 1
        let assign_x = eval.evaluate_unbound_statement(&Script::Var(ScriptToken::identifier("x"), Expression::Number(ScriptToken::number("1"))), &env);
        assert!(assign_x.is_ok());

        // x
        let val_of_x = eval.evaluate_unbound_statement(&Script::RunCommand(Expression::Identifier(ScriptToken::identifier("x"))), &env);
        assert!(val_of_x == Ok(json![ 1 ]));
    }

    #[test]
    fn can_bind_tool_from_passed_in_environment() {
        let eval    = StatefulEvalTool::new();
        let env     = DynamicEnvironment::new();

        env.define("test-tool", Box::new(make_pure_tool(|_: ()| 42)));

        // test-tool
        let val_of_test_tool = eval.evaluate_unbound_statement(&Script::RunCommand(Expression::Identifier(ScriptToken::identifier("test-tool"))), &env);
        assert!(val_of_test_tool == Ok(json![ 42 ]));
    }
}
<commit_msg>Add tool implementation for the stateful eval tool<commit_after>use std::sync::{Mutex, Arc};
use std::result::Result;

use serde::*;
use serde_json::*;
use gossyp_base::*;

use super::binding_environment::*;
use super::script_interpreter::*;
use super::bind_statement::*;
use super::evaluate_statement::*;
use super::script::*;
use super::bound_script::*;

///
/// Represents a tool that can be used to evaluate scripts and maintains
/// state (useful for evaluation in a REPL environment)
///
#[derive(Clone)]
pub struct StatefulEvalTool {
    binding:    Arc<Mutex<Box<BindingEnvironment>>>,
    execution:  Arc<Mutex<ScriptExecutionEnvironment>>
}

impl StatefulEvalTool {
    pub fn new() -> StatefulEvalTool {
        StatefulEvalTool {
            binding: Arc::new(Mutex::new(BindingEnvironment::new())),
            execution: Arc::new(Mutex::new(ScriptExecutionEnvironment::new()))
        }
    }

    ///
    /// Evaluates an unbound statement using this tool
    ///
    pub fn evaluate_unbound_statement(&self, script: &Script, environment: &Environment) -> Result<Value, Value> {
        self.evaluate_statement(&self.bind_statement(script, environment)?, environment)
    }

    ///
    /// Binds a statement to this tool
    ///
    pub fn bind_statement(&self, script: &Script, environment: &Environment) -> Result<BoundScript, Value> {
        // Merge the stuff in the external environment with the stored environment
        let our_environment             = &mut **self.binding.lock().unwrap();
        let their_environment           = BindingEnvironment::from_environment(environment);
        let mut combined_environment    = BindingEnvironment::combine(our_environment, &*their_environment);

        // Bind to the combined environments
        bind_statement(script, &mut *combined_environment)
    }

    ///
    /// Evaluates a statement in the environment represented by this tool
    ///
    pub fn evaluate_statement(&self, script: &BoundScript, environment: &Environment) -> Result<Value, Value> {
        evaluate_statement(script, environment, &mut *self.execution.lock().unwrap())
    }
}

impl Tool for StatefulEvalTool {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        let script = from_value::<Script>(input);
        self.evaluate_unbound_statement(script, environment)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn can_bind_variable_using_stateful_tool() {
        let eval    = StatefulEvalTool::new();
        let env     = EmptyEnvironment::new();

        // var x = 1
        let assign_x = eval.evaluate_unbound_statement(&Script::Var(ScriptToken::identifier("x"), Expression::Number(ScriptToken::number("1"))), &env);
        assert!(assign_x.is_ok());

        // x
        let val_of_x = eval.evaluate_unbound_statement(&Script::RunCommand(Expression::Identifier(ScriptToken::identifier("x"))), &env);
        assert!(val_of_x == Ok(json![ 1 ]));
    }

    #[test]
    fn can_bind_tool_from_passed_in_environment() {
        let eval    = StatefulEvalTool::new();
        let env     = DynamicEnvironment::new();

        env.define("test-tool", Box::new(make_pure_tool(|_: ()| 42)));

        // test-tool
        let val_of_test_tool = eval.evaluate_unbound_statement(&Script::RunCommand(Expression::Identifier(ScriptToken::identifier("test-tool"))), &env);
        assert!(val_of_test_tool == Ok(json![ 42 ]));
    }
}
<|endoftext|>


<commit_before>extern crate rand;
use std::io;
use std::f32;
use std::io::prelude::*;
use rand::thread_rng;
use rand::distributions::{IndependentSample, Range};

fn f( x: f32 ) -> f32 {
    1.0 / ( x * x + 1.0 )
}

fn trapezoids_v1( a: f32, b: f32, n: i32 ) -> f32 {
    let mut sum: f32 = 0.0;
    let h = ( b - a ) / n as f32;
    let xk = |k: i32| a + k as f32 * h;
    for i in ( 0 .. n - 1 ) {
        let ( x0, x1 ) = ( xk( i ), xk( i + 1 ) );
        sum += 0.5 * ( f( x0 ) + f( x1 ) ) * ( x1 - x0 );
    }
    sum
}

fn trapezoids_v2( a: f32, b: f32, n: i32 ) -> f32 {
    let mut sum: f32 = 0.0;
    let h = ( b - a ) / n as f32;
    let xk = |k: i32| a + k as f32 * h;
    for i in ( 1 .. n - 1 ) {
        sum += f( xk( i ) );
    }
    h * ( 0.5 * ( f( a ) + f( b ) ) + sum )
}

fn monte_carlo( a: f32, b: f32, n: i32 ) -> f32 {
    let between = Range::new( a, b );
    let mut rng = rand::thread_rng();
    let mut sum = 0.0;
    for _ in ( 0 .. n ) {
        let x = between.ind_sample( &mut rng );
        sum += f( x );
    }
    ( b - a ) * sum / n as f32
}

fn rectangle( a: f32, b: f32, n: i32 ) -> ( f32, f32, f32 ) {
    let ( mut s_left, mut s_middle, mut s_right ) = ( 0.0, 0.0, 0.0 );
    let rectangle_left = |x0: f32, x1: f32| {
        f( x0 ) * ( x1 - x0 )
    };
    let rectangle_middle = |x0: f32, x1: f32| {
        f( 0.5 * ( x0 + x1 ) ) * ( x1 - x0 )
    };
    let rectangle_right = |x0: f32, x1: f32| {
        f( x0 ) * ( x0 - x1 )
    };
    let h = ( b - a ) / n as f32;
    let xk = |k: i32| a + k as f32 * h;
    for i in ( 0 .. n - 1 ) {
        let ( x0, x1 ) = ( xk( i + 0 ), xk( i + 1 ) );
        s_left += rectangle_left( x0, x1 );
        s_middle += rectangle_middle( x0, x1 );
        s_right += rectangle_right( x1, x0 );
    }
    ( s_left, s_middle, s_right )
}

fn simpson( a: f32, b: f32 ) -> f32 {
    ( ( b - a ) / 6.0 ) * ( f( a ) + 4.0 * f ( 0.5 * ( a + b ) ) + f( b ) )
}

fn simpson_kosates( a: f32, b: f32, n: i32 ) -> f32 {
    let h = ( b - a ) / n as f32;
    let xk = |k: i32| a + k as f32 * h;
    let mut sum = 0.0;
    for i in ( 1 .. n - 1 ).filter( |&x| x % 2 == 1 ) {
        let ( x0, x1, x2 ) = ( xk( i - 1 ), xk( i ), xk( i + 1 ) );
        sum += f( x0 ) + 4.0 * f( x1 ) + f( x2 );
    }
    ( h / 3.0 ) * sum
}

fn gauss( a: f32, b: f32 ) -> f32 {
    let bma = b - a;
    let bpa = b + a;
    let bpa2 = 0.5 * bpa;
    let tst = 2.0 * ( 3.0 as f32 ).sqrt();
    let tst2 = bma / tst;
    0.5 * bma * ( f( bpa2 - tst2 ) + f( bpa2 + tst2 ) )
}

fn print_tuple( text: &str, tuple: (f32, f32) ) {
    let ( pi, eps ) = tuple;
    println!( "{} pi = {:.24}, eps = {:.24}", text, pi, eps );
}

fn main() {
    let ( a, b ) = ( 0.0, 1.0 );
    let mut buffer = String::new();
    print!( "Enter iterations: " );
    io::stdout().flush()
        .ok()
        .expect( "Can't flush!" );
    io::stdin().read_line( &mut buffer )
        .ok()
        .expect( "Can't read line!" );
    let n = match buffer.trim().parse() {
        Ok( value )  => value,
        Err( error ) => {
            println!( "[error] {}; using default value.", error );
            1000
        }
    };
    println!( "Iteration count = {}", n );
    let pi = f32::consts::PI;
    let peps = |x: f32| ( x, ( x - pi ).abs() );
    let tr1 = 4.0 * trapezoids_v1( a, b, n );
    let tr2 = 4.0 * trapezoids_v2( a, b, n );
    let mc = 4.0 * monte_carlo( a, b, n );
    let ( mut lr, mut mr, mut rr ) = rectangle( a, b, n );
    lr *= 4.0; mr *= 4.0; rr *= 4.0;
    let sm = 4.0 * simpson( a, b );
    let sk = 4.0 * simpson_kosates( a, b, n );
    let gm = 4.0 * gauss( a, b );
    println!( "[     pi constant] pi = {:.24}", pi );
    print_tuple( "[   trapezoids v1]", peps( tr1 ) );
    print_tuple( "[   trapezoids v2]", peps( tr2 ) );
    print_tuple( "[     monte-carlo]", peps( mc ) );
    print_tuple( "[  rectangle left]", peps( lr ) );
    print_tuple( "[rectangle middle]", peps( mr ) );
    print_tuple( "[ rectangle right]", peps( rr ) );
    print_tuple( "[         simpson]", peps( sm ) );
    print_tuple( "[ simpson-kosates]", peps( sk ) );
    print_tuple( "[           gauss]", peps( gm ) );
}
<commit_msg>[upd] precision<commit_after>extern crate rand;
use std::io;
use std::f64;
use std::io::prelude::*;
use rand::thread_rng;
use rand::distributions::{IndependentSample, Range};

fn f( x: f64 ) -> f64 {
    1.0 / ( x * x + 1.0 )
}

fn trapezoids_v1( a: f64, b: f64, n: i64 ) -> f64 {
    let mut sum: f64 = 0.0;
    let h = ( b - a ) / n as f64;
    let xk = |k: i64| a + k as f64 * h;
    for i in ( 0 .. n - 1 ) {
        let ( x0, x1 ) = ( xk( i ), xk( i + 1 ) );
        sum += 0.5 * ( f( x0 ) + f( x1 ) ) * ( x1 - x0 );
    }
    sum
}

fn trapezoids_v2( a: f64, b: f64, n: i64 ) -> f64 {
    let mut sum: f64 = 0.0;
    let h = ( b - a ) / n as f64;
    let xk = |k: i64| a + k as f64 * h;
    for i in ( 1 .. n - 1 ) {
        sum += f( xk( i ) );
    }
    h * ( 0.5 * ( f( a ) + f( b ) ) + sum )
}

fn monte_carlo( a: f64, b: f64, n: i64 ) -> f64 {
    let between = Range::new( a, b );
    let mut rng = rand::thread_rng();
    let mut sum = 0.0;
    for _ in ( 0 .. n ) {
        let x = between.ind_sample( &mut rng );
        sum += f( x );
    }
    ( b - a ) * sum / n as f64
}

fn rectangle( a: f64, b: f64, n: i64 ) -> ( f64, f64, f64 ) {
    let ( mut s_left, mut s_middle, mut s_right ) = ( 0.0, 0.0, 0.0 );
    let rectangle_left = |x0: f64, x1: f64| {
        f( x0 ) * ( x1 - x0 )
    };
    let rectangle_middle = |x0: f64, x1: f64| {
        f( 0.5 * ( x0 + x1 ) ) * ( x1 - x0 )
    };
    let rectangle_right = |x0: f64, x1: f64| {
        f( x0 ) * ( x0 - x1 )
    };
    let h = ( b - a ) / n as f64;
    let xk = |k: i64| a + k as f64 * h;
    for i in ( 0 .. n - 1 ) {
        let ( x0, x1 ) = ( xk( i + 0 ), xk( i + 1 ) );
        s_left += rectangle_left( x0, x1 );
        s_middle += rectangle_middle( x0, x1 );
        s_right += rectangle_right( x1, x0 );
    }
    ( s_left, s_middle, s_right )
}

fn simpson( a: f64, b: f64 ) -> f64 {
    ( ( b - a ) / 6.0 ) * ( f( a ) + 4.0 * f ( 0.5 * ( a + b ) ) + f( b ) )
}

fn simpson_kosates( a: f64, b: f64, n: i64 ) -> f64 {
    let h = ( b - a ) / n as f64;
    let xk = |k: i64| a + k as f64 * h;
    let mut sum = 0.0;
    for i in ( 1 .. n - 1 ).filter( |&x| x % 2 == 1 ) {
        let ( x0, x1, x2 ) = ( xk( i - 1 ), xk( i ), xk( i + 1 ) );
        sum += f( x0 ) + 4.0 * f( x1 ) + f( x2 );
    }
    ( h / 3.0 ) * sum
}

fn gauss( a: f64, b: f64 ) -> f64 {
    let bma = b - a;
    let bpa = b + a;
    let bpa2 = 0.5 * bpa;
    let tst = 2.0 * ( 3.0 as f64 ).sqrt();
    let tst2 = bma / tst;
    0.5 * bma * ( f( bpa2 - tst2 ) + f( bpa2 + tst2 ) )
}

fn print_tuple( text: &str, tuple: (f64, f64) ) {
    let ( pi, eps ) = tuple;
    println!( "{} pi = {:.50}, eps = {:.50}", text, pi, eps );
}

fn main() {
    let ( a, b ) = ( 0.0, 1.0 );
    let mut buffer = String::new();
    print!( "Enter iterations: " );
    io::stdout().flush()
        .ok()
        .expect( "Can't flush!" );
    io::stdin().read_line( &mut buffer )
        .ok()
        .expect( "Can't read line!" );
    let n = match buffer.trim().parse() {
        Ok( value )  => value,
        Err( error ) => {
            println!( "[error] {}; using default value.", error );
            1000
        }
    };
    println!( "Iteration count = {}", n );
    let pi = f64::consts::PI;
    let peps = |x: f64| ( x, ( x - pi ).abs() );
    let tr1 = 4.0 * trapezoids_v1( a, b, n );
    let tr2 = 4.0 * trapezoids_v2( a, b, n );
    let mc = 4.0 * monte_carlo( a, b, n );
    let ( mut lr, mut mr, mut rr ) = rectangle( a, b, n );
    lr *= 4.0; mr *= 4.0; rr *= 4.0;
    let sm = 4.0 * simpson( a, b );
    let sk = 4.0 * simpson_kosates( a, b, n );
    let gm = 4.0 * gauss( a, b );
    println!( "[     pi constant] pi = {:.50}", pi );
    print_tuple( "[   trapezoids v1]", peps( tr1 ) );
    print_tuple( "[   trapezoids v2]", peps( tr2 ) );
    print_tuple( "[     monte-carlo]", peps( mc ) );
    print_tuple( "[  rectangle left]", peps( lr ) );
    print_tuple( "[rectangle middle]", peps( mr ) );
    print_tuple( "[ rectangle right]", peps( rr ) );
    print_tuple( "[         simpson]", peps( sm ) );
    print_tuple( "[ simpson-kosates]", peps( sk ) );
    print_tuple( "[           gauss]", peps( gm ) );
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.

use std::cmp;
use std::collections::{BinaryHeap, HashMap};
use std::io;
use std::mem;
use std::sync::{atomic, mpsc};
use std::sync::{Arc, Mutex, Weak};
use std::thread;
use std::time;

use futures::future;
use futures::stream::futures_unordered::FuturesUnordered;
use futures::sync::mpsc as futures_mpsc;
use futures::sync::oneshot;
use futures::{Async, Future, Stream};
use tokio::reactor;
use tokio_current_thread;
use tokio_timer::timer;

use glib;
use gst;

lazy_static! {
    static ref CONTEXTS: Mutex<HashMap<String, Weak<IOContextInner>>> = Mutex::new(HashMap::new());
    static ref CONTEXT_CAT: gst::DebugCategory = gst::DebugCategory::new(
        "ts-context",
        gst::DebugColorFlags::empty(),
        Some("Thread-sharing Context"),
    );
}

// Our own simplified implementation of reactor::Background to allow hooking into its internals
const RUNNING: usize = 0;
const SHUTDOWN_NOW: usize = 1;

struct IOContextRunner {
    name: String,
    shutdown: Arc<atomic::AtomicUsize>,
}

impl IOContextRunner {
    fn start(
        name: &str,
        wait: u32,
        reactor: reactor::Reactor,
        timers: Arc<Mutex<BinaryHeap<TimerEntry>>>,
    ) -> (tokio_current_thread::Handle, IOContextShutdown) {
        let handle = reactor.handle().clone();
        let shutdown = Arc::new(atomic::AtomicUsize::new(RUNNING));
        let shutdown_clone = shutdown.clone();
        let name_clone = name.into();

        let mut runner = IOContextRunner {
            shutdown: shutdown_clone,
            name: name_clone,
        };

        let (sender, receiver) = mpsc::channel();

        let join = thread::spawn(move || {
            runner.run(wait, reactor, sender, timers);
        });

        let shutdown = IOContextShutdown {
            name: name.into(),
            shutdown,
            handle,
            join: Some(join),
        };

        let runtime_handle = receiver.recv().unwrap();

        (runtime_handle, shutdown)
    }

    fn run(
        &mut self,
        wait: u32,
        reactor: reactor::Reactor,
        sender: mpsc::Sender<tokio_current_thread::Handle>,
        timers: Arc<Mutex<BinaryHeap<TimerEntry>>>,
    ) {
        let wait = time::Duration::from_millis(wait as u64);

        gst_debug!(CONTEXT_CAT, "Started reactor thread '{}'", self.name);

        let handle = reactor.handle();
        let mut enter = ::tokio_executor::enter().unwrap();
        let timer = timer::Timer::new(reactor);
        let timer_handle = timer.handle();
        let mut current_thread = tokio_current_thread::CurrentThread::new_with_park(timer);

        let _ = sender.send(current_thread.handle());

        let mut now = time::Instant::now();

        ::tokio_timer::with_default(&timer_handle, &mut enter, |mut enter| {
            ::tokio_reactor::with_default(&handle, &mut enter, |enter| loop {
                if self.shutdown.load(atomic::Ordering::SeqCst) > RUNNING {
                    break;
                }

                gst_trace!(CONTEXT_CAT, "Elapsed {:?} since last loop", now.elapsed());

                // Handle timers
                {
                    // Trigger all timers that would be expired before the middle of the loop wait
                    // time
                    let timer_threshold = now + wait / 2;
                    let mut timers = timers.lock().unwrap();
                    while timers
                        .peek()
                        .and_then(|entry| {
                            if entry.time < timer_threshold {
                                Some(())
                            } else {
                                None
                            }
                        })
                        .is_some()
                    {
                        let TimerEntry {
                            time,
                            interval,
                            sender,
                            ..
                        } = timers.pop().unwrap();

                        if sender.is_closed() {
                            continue;
                        }

                        let _ = sender.unbounded_send(());
                        if let Some(interval) = interval {
                            timers.push(TimerEntry {
                                time: time + interval,
                                id: TIMER_ENTRY_ID.fetch_add(1, atomic::Ordering::Relaxed),
                                interval: Some(interval),
                                sender,
                            });
                        }
                    }
                }

                gst_trace!(CONTEXT_CAT, "Turning current thread '{}'", self.name);
                while current_thread
                    .enter(enter)
                    .turn(Some(time::Duration::from_millis(0)))
                    .unwrap()
                    .has_polled()
                {}
                gst_trace!(CONTEXT_CAT, "Turned current thread '{}'", self.name);

                let elapsed = now.elapsed();
                gst_trace!(CONTEXT_CAT, "Elapsed {:?} after handling futures", elapsed);

                if wait == time::Duration::from_millis(0) {
                    let timers = timers.lock().unwrap();
                    let wait = match timers.peek().map(|entry| entry.time) {
                        None => None,
                        Some(time) => Some({
                            let tmp = time::Instant::now();

                            if time < tmp {
                                time::Duration::from_millis(0)
                            } else {
                                time.duration_since(tmp)
                            }
                        }),
                    };

                    gst_trace!(CONTEXT_CAT, "Sleeping for up to {:?}", wait);
                    current_thread.enter(enter).turn(wait).unwrap();
                    gst_trace!(CONTEXT_CAT, "Slept for {:?}", now.elapsed());
                    now = time::Instant::now();
                } else {
                    if elapsed < wait {
                        gst_trace!(
                            CONTEXT_CAT,
                            "Waiting for {:?} before polling again",
                            wait - elapsed
                        );
                        thread::sleep(wait - elapsed);
                        gst_trace!(CONTEXT_CAT, "Slept for {:?}", now.elapsed());
                    }

                    now += wait;
                }
            })
        });
    }
}

impl Drop for IOContextRunner {
    fn drop(&mut self) {
        gst_debug!(CONTEXT_CAT, "Shut down reactor thread '{}'", self.name);
    }
}

struct IOContextShutdown {
    name: String,
    shutdown: Arc<atomic::AtomicUsize>,
    handle: reactor::Handle,
    join: Option<thread::JoinHandle<()>>,
}

impl Drop for IOContextShutdown {
    fn drop(&mut self) {
        use tokio_executor::park::Unpark;

        gst_debug!(CONTEXT_CAT, "Shutting down reactor thread '{}'", self.name);
        self.shutdown.store(SHUTDOWN_NOW, atomic::Ordering::SeqCst);
        gst_trace!(CONTEXT_CAT, "Waiting for reactor '{}' shutdown", self.name);
        // After being unparked, the next turn() is guaranteed to finish immediately,
        // as such there is no race condition between checking for shutdown and setting
        // shutdown.
        self.handle.unpark();
        let _ = self.join.take().unwrap().join();
    }
}

#[derive(Clone)]
pub struct IOContext(Arc<IOContextInner>);

impl glib::subclass::boxed::BoxedType for IOContext {
    const NAME: &'static str = "TsIOContext";

    glib_boxed_type!();
}

glib_boxed_derive_traits!(IOContext);

type PendingFutures = Mutex<(
    u64,
    HashMap<u64, FuturesUnordered<Box<dyn Future<Item = (), Error = ()> + Send + 'static>>>,
)>;

struct IOContextInner {
    name: String,
    runtime_handle: Mutex<tokio_current_thread::Handle>,
    reactor_handle: reactor::Handle,
    timers: Arc<Mutex<BinaryHeap<TimerEntry>>>,
    // Only used for dropping
    _shutdown: IOContextShutdown,
    pending_futures: PendingFutures,
}

impl Drop for IOContextInner {
    fn drop(&mut self) {
        let mut contexts = CONTEXTS.lock().unwrap();
        gst_debug!(CONTEXT_CAT, "Finalizing context '{}'", self.name);
        contexts.remove(&self.name);
    }
}

impl IOContext {
    pub fn new(name: &str, wait: u32) -> Result<Self, io::Error> {
        let mut contexts = CONTEXTS.lock().unwrap();
        if let Some(context) = contexts.get(name) {
            if let Some(context) = context.upgrade() {
                gst_debug!(CONTEXT_CAT, "Reusing existing context '{}'", name);
                return Ok(IOContext(context));
            }
        }

        let reactor = reactor::Reactor::new()?;
        let reactor_handle = reactor.handle().clone();

        let timers = Arc::new(Mutex::new(BinaryHeap::new()));

        let (runtime_handle, shutdown) =
            IOContextRunner::start(name, wait, reactor, timers.clone());

        let context = Arc::new(IOContextInner {
            name: name.into(),
            runtime_handle: Mutex::new(runtime_handle),
            reactor_handle,
            timers,
            _shutdown: shutdown,
            pending_futures: Mutex::new((0, HashMap::new())),
        });
        contexts.insert(name.into(), Arc::downgrade(&context));

        gst_debug!(CONTEXT_CAT, "Created new context '{}'", name);
        Ok(IOContext(context))
    }

    pub fn spawn<F>(&self, future: F)
    where
        F: Future<Item = (), Error = ()> + Send + 'static,
    {
        self.0.runtime_handle.lock().unwrap().spawn(future).unwrap();
    }

    pub fn reactor_handle(&self) -> &reactor::Handle {
        &self.0.reactor_handle
    }

    pub fn acquire_pending_future_id(&self) -> PendingFutureId {
        let mut pending_futures = self.0.pending_futures.lock().unwrap();
        let id = pending_futures.0;
        pending_futures.0 += 1;
        pending_futures.1.insert(id, FuturesUnordered::new());

        PendingFutureId(id)
    }

    pub fn release_pending_future_id(&self, id: PendingFutureId) {
        let mut pending_futures = self.0.pending_futures.lock().unwrap();
        if let Some(fs) = pending_futures.1.remove(&id.0) {
            self.spawn(fs.for_each(|_| Ok(())));
        }
    }

    pub fn add_pending_future<F>(&self, id: PendingFutureId, future: F)
    where
        F: Future<Item = (), Error = ()> + Send + 'static,
    {
        let mut pending_futures = self.0.pending_futures.lock().unwrap();
        let fs = pending_futures.1.get_mut(&id.0).unwrap();
        fs.push(Box::new(future))
    }

    pub fn drain_pending_futures<E: Send + 'static>(
        &self,
        id: PendingFutureId,
    ) -> (Option<oneshot::Sender<()>>, PendingFuturesFuture<E>) {
        let mut pending_futures = self.0.pending_futures.lock().unwrap();
        let fs = pending_futures.1.get_mut(&id.0).unwrap();

        let pending_futures = mem::replace(fs, FuturesUnordered::new());

        if !pending_futures.is_empty() {
            gst_log!(
                CONTEXT_CAT,
                "Scheduling {} pending futures for context '{}' with pending future id {:?}",
                pending_futures.len(),
                self.0.name,
                id,
            );

            let (sender, receiver) = oneshot::channel();

            let future = pending_futures
                .for_each(|_| Ok(()))
                .select(receiver.then(|_| Ok(())))
                .then(|_| Ok(()));

            (Some(sender), future::Either::A(Box::new(future)))
        } else {
            (None, future::Either::B(future::ok(())))
        }
    }
}

pub type PendingFuturesFuture<E> = future::Either<
    Box<dyn Future<Item = (), Error = E> + Send + 'static>,
    future::FutureResult<(), E>,
>;

#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub struct PendingFutureId(u64);

impl glib::subclass::boxed::BoxedType for PendingFutureId {
    const NAME: &'static str = "TsPendingFutureId";

    glib_boxed_type!();
}

glib_boxed_derive_traits!(PendingFutureId);

static TIMER_ENTRY_ID: atomic::AtomicUsize = atomic::AtomicUsize::new(0);

// Ad-hoc interval timer implementation for our throttled event loop above
pub struct TimerEntry {
    time: time::Instant,
    id: usize, // for producing a total order
    interval: Option<time::Duration>,
    sender: futures_mpsc::UnboundedSender<()>,
}

impl PartialEq for TimerEntry {
    fn eq(&self, other: &Self) -> bool {
        self.time.eq(&other.time) && self.id.eq(&other.id)
    }
}

impl Eq for TimerEntry {}

impl PartialOrd for TimerEntry {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.cmp(&other))
    }
}

impl Ord for TimerEntry {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        other
            .time
            .cmp(&self.time)
            .then_with(|| other.id.cmp(&self.id))
    }
}

#[allow(unused)]
pub struct Interval {
    receiver: futures_mpsc::UnboundedReceiver<()>,
}

impl Interval {
    #[allow(unused)]
    pub fn new(context: &IOContext, interval: time::Duration) -> Self {
        use tokio_executor::park::Unpark;

        let (sender, receiver) = futures_mpsc::unbounded();

        let mut timers = context.0.timers.lock().unwrap();
        let entry = TimerEntry {
            time: time::Instant::now(),
            id: TIMER_ENTRY_ID.fetch_add(1, atomic::Ordering::Relaxed),
            interval: Some(interval),
            sender,
        };
        timers.push(entry);
        context.reactor_handle().unpark();

        Self { receiver }
    }
}

impl Stream for Interval {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> {
        self.receiver.poll()
    }
}

pub struct Timeout {
    receiver: futures_mpsc::UnboundedReceiver<()>,
}

impl Timeout {
    pub fn new(context: &IOContext, timeout: time::Duration) -> Self {
        let (sender, receiver) = futures_mpsc::unbounded();

        let mut timers = context.0.timers.lock().unwrap();
        let entry = TimerEntry {
            time: time::Instant::now() + timeout,
            id: TIMER_ENTRY_ID.fetch_add(1, atomic::Ordering::Relaxed),
            interval: None,
            sender,
        };
        timers.push(entry);

        Self { receiver }
    }
}

impl Future for Timeout {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
        let res = self.receiver.poll()?;

        match res {
            Async::NotReady => Ok(Async::NotReady),
            Async::Ready(None) => unreachable!(),
            Async::Ready(Some(_)) => Ok(Async::Ready(())),
        }
    }
}
<commit_msg>threadshare: Don't keep timers locked while waiting for IO<commit_after>// Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.

use std::cmp;
use std::collections::{BinaryHeap, HashMap};
use std::io;
use std::mem;
use std::sync::{atomic, mpsc};
use std::sync::{Arc, Mutex, Weak};
use std::thread;
use std::time;

use futures::future;
use futures::stream::futures_unordered::FuturesUnordered;
use futures::sync::mpsc as futures_mpsc;
use futures::sync::oneshot;
use futures::{Async, Future, Stream};
use tokio::reactor;
use tokio_current_thread;
use tokio_timer::timer;

use glib;
use gst;

lazy_static! {
    static ref CONTEXTS: Mutex<HashMap<String, Weak<IOContextInner>>> = Mutex::new(HashMap::new());
    static ref CONTEXT_CAT: gst::DebugCategory = gst::DebugCategory::new(
        "ts-context",
        gst::DebugColorFlags::empty(),
        Some("Thread-sharing Context"),
    );
}

// Our own simplified implementation of reactor::Background to allow hooking into its internals
const RUNNING: usize = 0;
const SHUTDOWN_NOW: usize = 1;

struct IOContextRunner {
    name: String,
    shutdown: Arc<atomic::AtomicUsize>,
}

impl IOContextRunner {
    fn start(
        name: &str,
        wait: u32,
        reactor: reactor::Reactor,
        timers: Arc<Mutex<BinaryHeap<TimerEntry>>>,
    ) -> (tokio_current_thread::Handle, IOContextShutdown) {
        let handle = reactor.handle().clone();
        let shutdown = Arc::new(atomic::AtomicUsize::new(RUNNING));
        let shutdown_clone = shutdown.clone();
        let name_clone = name.into();

        let mut runner = IOContextRunner {
            shutdown: shutdown_clone,
            name: name_clone,
        };

        let (sender, receiver) = mpsc::channel();

        let join = thread::spawn(move || {
            runner.run(wait, reactor, sender, timers);
        });

        let shutdown = IOContextShutdown {
            name: name.into(),
            shutdown,
            handle,
            join: Some(join),
        };

        let runtime_handle = receiver.recv().unwrap();

        (runtime_handle, shutdown)
    }

    fn run(
        &mut self,
        wait: u32,
        reactor: reactor::Reactor,
        sender: mpsc::Sender<tokio_current_thread::Handle>,
        timers: Arc<Mutex<BinaryHeap<TimerEntry>>>,
    ) {
        let wait = time::Duration::from_millis(wait as u64);

        gst_debug!(CONTEXT_CAT, "Started reactor thread '{}'", self.name);

        let handle = reactor.handle();
        let mut enter = ::tokio_executor::enter().unwrap();
        let timer = timer::Timer::new(reactor);
        let timer_handle = timer.handle();
        let mut current_thread = tokio_current_thread::CurrentThread::new_with_park(timer);

        let _ = sender.send(current_thread.handle());

        let mut now = time::Instant::now();

        ::tokio_timer::with_default(&timer_handle, &mut enter, |mut enter| {
            ::tokio_reactor::with_default(&handle, &mut enter, |enter| loop {
                if self.shutdown.load(atomic::Ordering::SeqCst) > RUNNING {
                    break;
                }

                gst_trace!(CONTEXT_CAT, "Elapsed {:?} since last loop", now.elapsed());

                // Handle timers
                {
                    // Trigger all timers that would be expired before the middle of the loop wait
                    // time
                    let timer_threshold = now + wait / 2;
                    let mut timers = timers.lock().unwrap();
                    while timers
                        .peek()
                        .and_then(|entry| {
                            if entry.time < timer_threshold {
                                Some(())
                            } else {
                                None
                            }
                        })
                        .is_some()
                    {
                        let TimerEntry {
                            time,
                            interval,
                            sender,
                            ..
                        } = timers.pop().unwrap();

                        if sender.is_closed() {
                            continue;
                        }

                        let _ = sender.unbounded_send(());
                        if let Some(interval) = interval {
                            timers.push(TimerEntry {
                                time: time + interval,
                                id: TIMER_ENTRY_ID.fetch_add(1, atomic::Ordering::Relaxed),
                                interval: Some(interval),
                                sender,
                            });
                        }
                    }
                }

                gst_trace!(CONTEXT_CAT, "Turning current thread '{}'", self.name);
                while current_thread
                    .enter(enter)
                    .turn(Some(time::Duration::from_millis(0)))
                    .unwrap()
                    .has_polled()
                {}
                gst_trace!(CONTEXT_CAT, "Turned current thread '{}'", self.name);

                let elapsed = now.elapsed();
                gst_trace!(CONTEXT_CAT, "Elapsed {:?} after handling futures", elapsed);

                if wait == time::Duration::from_millis(0) {
                    let timers = timers.lock().unwrap();
                    let wait = match timers.peek().map(|entry| entry.time) {
                        None => None,
                        Some(time) => Some({
                            let tmp = time::Instant::now();

                            if time < tmp {
                                time::Duration::from_millis(0)
                            } else {
                                time.duration_since(tmp)
                            }
                        }),
                    };
                    drop(timers);

                    gst_trace!(CONTEXT_CAT, "Sleeping for up to {:?}", wait);
                    current_thread.enter(enter).turn(wait).unwrap();
                    gst_trace!(CONTEXT_CAT, "Slept for {:?}", now.elapsed());
                    now = time::Instant::now();
                } else {
                    if elapsed < wait {
                        gst_trace!(
                            CONTEXT_CAT,
                            "Waiting for {:?} before polling again",
                            wait - elapsed
                        );
                        thread::sleep(wait - elapsed);
                        gst_trace!(CONTEXT_CAT, "Slept for {:?}", now.elapsed());
                    }

                    now += wait;
                }
            })
        });
    }
}

impl Drop for IOContextRunner {
    fn drop(&mut self) {
        gst_debug!(CONTEXT_CAT, "Shut down reactor thread '{}'", self.name);
    }
}

struct IOContextShutdown {
    name: String,
    shutdown: Arc<atomic::AtomicUsize>,
    handle: reactor::Handle,
    join: Option<thread::JoinHandle<()>>,
}

impl Drop for IOContextShutdown {
    fn drop(&mut self) {
        use tokio_executor::park::Unpark;

        gst_debug!(CONTEXT_CAT, "Shutting down reactor thread '{}'", self.name);
        self.shutdown.store(SHUTDOWN_NOW, atomic::Ordering::SeqCst);
        gst_trace!(CONTEXT_CAT, "Waiting for reactor '{}' shutdown", self.name);
        // After being unparked, the next turn() is guaranteed to finish immediately,
        // as such there is no race condition between checking for shutdown and setting
        // shutdown.
        self.handle.unpark();
        let _ = self.join.take().unwrap().join();
    }
}

#[derive(Clone)]
pub struct IOContext(Arc<IOContextInner>);

impl glib::subclass::boxed::BoxedType for IOContext {
    const NAME: &'static str = "TsIOContext";

    glib_boxed_type!();
}

glib_boxed_derive_traits!(IOContext);

type PendingFutures = Mutex<(
    u64,
    HashMap<u64, FuturesUnordered<Box<dyn Future<Item = (), Error = ()> + Send + 'static>>>,
)>;

struct IOContextInner {
    name: String,
    runtime_handle: Mutex<tokio_current_thread::Handle>,
    reactor_handle: reactor::Handle,
    timers: Arc<Mutex<BinaryHeap<TimerEntry>>>,
    // Only used for dropping
    _shutdown: IOContextShutdown,
    pending_futures: PendingFutures,
}

impl Drop for IOContextInner {
    fn drop(&mut self) {
        let mut contexts = CONTEXTS.lock().unwrap();
        gst_debug!(CONTEXT_CAT, "Finalizing context '{}'", self.name);
        contexts.remove(&self.name);
    }
}

impl IOContext {
    pub fn new(name: &str, wait: u32) -> Result<Self, io::Error> {
        let mut contexts = CONTEXTS.lock().unwrap();
        if let Some(context) = contexts.get(name) {
            if let Some(context) = context.upgrade() {
                gst_debug!(CONTEXT_CAT, "Reusing existing context '{}'", name);
                return Ok(IOContext(context));
            }
        }

        let reactor = reactor::Reactor::new()?;
        let reactor_handle = reactor.handle().clone();

        let timers = Arc::new(Mutex::new(BinaryHeap::new()));

        let (runtime_handle, shutdown) =
            IOContextRunner::start(name, wait, reactor, timers.clone());

        let context = Arc::new(IOContextInner {
            name: name.into(),
            runtime_handle: Mutex::new(runtime_handle),
            reactor_handle,
            timers,
            _shutdown: shutdown,
            pending_futures: Mutex::new((0, HashMap::new())),
        });
        contexts.insert(name.into(), Arc::downgrade(&context));

        gst_debug!(CONTEXT_CAT, "Created new context '{}'", name);
        Ok(IOContext(context))
    }

    pub fn spawn<F>(&self, future: F)
    where
        F: Future<Item = (), Error = ()> + Send + 'static,
    {
        self.0.runtime_handle.lock().unwrap().spawn(future).unwrap();
    }

    pub fn reactor_handle(&self) -> &reactor::Handle {
        &self.0.reactor_handle
    }

    pub fn acquire_pending_future_id(&self) -> PendingFutureId {
        let mut pending_futures = self.0.pending_futures.lock().unwrap();
        let id = pending_futures.0;
        pending_futures.0 += 1;
        pending_futures.1.insert(id, FuturesUnordered::new());

        PendingFutureId(id)
    }

    pub fn release_pending_future_id(&self, id: PendingFutureId) {
        let mut pending_futures = self.0.pending_futures.lock().unwrap();
        if let Some(fs) = pending_futures.1.remove(&id.0) {
            self.spawn(fs.for_each(|_| Ok(())));
        }
    }

    pub fn add_pending_future<F>(&self, id: PendingFutureId, future: F)
    where
        F: Future<Item = (), Error = ()> + Send + 'static,
    {
        let mut pending_futures = self.0.pending_futures.lock().unwrap();
        let fs = pending_futures.1.get_mut(&id.0).unwrap();
        fs.push(Box::new(future))
    }

    pub fn drain_pending_futures<E: Send + 'static>(
        &self,
        id: PendingFutureId,
    ) -> (Option<oneshot::Sender<()>>, PendingFuturesFuture<E>) {
        let mut pending_futures = self.0.pending_futures.lock().unwrap();
        let fs = pending_futures.1.get_mut(&id.0).unwrap();

        let pending_futures = mem::replace(fs, FuturesUnordered::new());

        if !pending_futures.is_empty() {
            gst_log!(
                CONTEXT_CAT,
                "Scheduling {} pending futures for context '{}' with pending future id {:?}",
                pending_futures.len(),
                self.0.name,
                id,
            );

            let (sender, receiver) = oneshot::channel();

            let future = pending_futures
                .for_each(|_| Ok(()))
                .select(receiver.then(|_| Ok(())))
                .then(|_| Ok(()));

            (Some(sender), future::Either::A(Box::new(future)))
        } else {
            (None, future::Either::B(future::ok(())))
        }
    }
}

pub type PendingFuturesFuture<E> = future::Either<
    Box<dyn Future<Item = (), Error = E> + Send + 'static>,
    future::FutureResult<(), E>,
>;

#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub struct PendingFutureId(u64);

impl glib::subclass::boxed::BoxedType for PendingFutureId {
    const NAME: &'static str = "TsPendingFutureId";

    glib_boxed_type!();
}

glib_boxed_derive_traits!(PendingFutureId);

static TIMER_ENTRY_ID: atomic::AtomicUsize = atomic::AtomicUsize::new(0);

// Ad-hoc interval timer implementation for our throttled event loop above
pub struct TimerEntry {
    time: time::Instant,
    id: usize, // for producing a total order
    interval: Option<time::Duration>,
    sender: futures_mpsc::UnboundedSender<()>,
}

impl PartialEq for TimerEntry {
    fn eq(&self, other: &Self) -> bool {
        self.time.eq(&other.time) && self.id.eq(&other.id)
    }
}

impl Eq for TimerEntry {}

impl PartialOrd for TimerEntry {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.cmp(&other))
    }
}

impl Ord for TimerEntry {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        other
            .time
            .cmp(&self.time)
            .then_with(|| other.id.cmp(&self.id))
    }
}

#[allow(unused)]
pub struct Interval {
    receiver: futures_mpsc::UnboundedReceiver<()>,
}

impl Interval {
    #[allow(unused)]
    pub fn new(context: &IOContext, interval: time::Duration) -> Self {
        use tokio_executor::park::Unpark;

        let (sender, receiver) = futures_mpsc::unbounded();

        let mut timers = context.0.timers.lock().unwrap();
        let entry = TimerEntry {
            time: time::Instant::now(),
            id: TIMER_ENTRY_ID.fetch_add(1, atomic::Ordering::Relaxed),
            interval: Some(interval),
            sender,
        };
        timers.push(entry);
        context.reactor_handle().unpark();

        Self { receiver }
    }
}

impl Stream for Interval {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> {
        self.receiver.poll()
    }
}

pub struct Timeout {
    receiver: futures_mpsc::UnboundedReceiver<()>,
}

impl Timeout {
    pub fn new(context: &IOContext, timeout: time::Duration) -> Self {
        let (sender, receiver) = futures_mpsc::unbounded();

        let mut timers = context.0.timers.lock().unwrap();
        let entry = TimerEntry {
            time: time::Instant::now() + timeout,
            id: TIMER_ENTRY_ID.fetch_add(1, atomic::Ordering::Relaxed),
            interval: None,
            sender,
        };
        timers.push(entry);

        Self { receiver }
    }
}

impl Future for Timeout {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
        let res = self.receiver.poll()?;

        match res {
            Async::NotReady => Ok(Async::NotReady),
            Async::Ready(None) => unreachable!(),
            Async::Ready(Some(_)) => Ok(Async::Ready(())),
        }
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate extra;

use std::env;
use std::fs;
use std::io::{stdout, stderr, Write};
use extra::io::fail;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{rm} */ r#"
NAME
    rm - delete files

SYNOPSIS
    rm [ -h | --help ] FILE...

DESCRIPTION
    The rm utility deletes the file named by the FILE operand. Multiple file names can be passed.

OPTIONS
    --help, -h
        print this message
"#; /* @MANEND */

fn main() {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();

    if env::args().count() == 2 {
        if let Some(arg) = env::args().nth(1) {
            if arg == "--help" || arg == "-h" {
                stdout.write_all(MAN_PAGE.as_bytes()).try(&mut stderr);
                stdout.flush().try(&mut stderr);
                return;
            }
        }
    }

    if env::args().count() < 2 {
        fail("No arguments. Use --help to see the usage.", &mut stderr);
    }

    for ref path in env::args().skip(1) {
        fs::remove_file(path).try(&mut stderr);
    }
}
<commit_msg>Improve Implementation of Remove<commit_after>#![deny(warnings)]
extern crate extra;

use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, Write, Stderr, Stdout, StdoutLock};
use std::path::{Path, PathBuf};
use std::process::exit;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{rm} */ r#"NAME
    rm - remove files and directories

SYNOPSIS
    rm [-h | --help] [-d | --dir] [-i] [-r | -R | --recursive] [-v | --verbose] TARGETS...

DESCRIPTION
    Removes each specified file, but does not remove directories by default.

OPTIONS
    -h
    --help
        Display this help information and exit.

    -d
    --dir
        Remove empty directories in addition to files.

    -i
        Prompt before removing all files and directories.

    -r
    -R
    --recursive
        Remove directories and their contents recursively.

    -v
    --verbose
        Print the file changes that have been successfully performed.

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

fn main() {
    let stdout = io::stdout();
    let mut rm = Program::initialize(&stdout);
    rm.execute();
}

struct Program<'a> {
    arguments:   Vec<PathBuf>,
    directory:   bool,
    interactive: bool,
    recursive:   bool,
    verbose:     bool,
    stderr:      Stderr,
    stdout:      StdoutLock<'a>,
}

impl<'a> Program<'a> {
    /// Initialize the program's arguments and check if the paths are valid.
    fn initialize(stdout: &'a Stdout) -> Program<'a> {
        let mut rm = Program {
            arguments:   Vec::new(),
            directory:   false,
            interactive: false,
            recursive:   false,
            verbose:     false,
            stderr:      io::stderr(),
            stdout:      stdout.lock()
        };
        for argument in env::args().skip(1).collect::<Vec<String>>() {
            if argument.starts_with("--") {
                match argument.as_str() {
                    "--dir" => rm.directory = true,
                    "--help" => {
                        rm.stdout.write(MAN_PAGE.as_bytes()).try(&mut rm.stderr);
                        rm.stdout.flush().try(&mut rm.stderr);
                        exit(0);
                    },
                    "--recursive" => {
                        rm.directory = true;
                        rm.recursive = true;
                    },
                    "--verbose" => rm.verbose = true,
                    _ => {
                        rm.stderr.write(b"invalid argument '").try(&mut rm.stderr);
                        rm.stderr.write(argument.as_bytes()).try(&mut rm.stderr);
                        rm.stderr.write(b"'\n").try(&mut rm.stderr);
                        rm.stderr.flush().try(&mut rm.stderr);
                    }
                }
            } else if argument.starts_with("-") {
                for character in argument.chars().skip(1) {
                    match character {
                        'd' => rm.directory = true,
                        'h' => {
                            rm.stdout.write(MAN_PAGE.as_bytes()).try(&mut rm.stderr);
                            rm.stdout.flush().try(&mut rm.stderr);
                            exit(0);
                        },
                        'i' => rm.interactive = true,
                        'r' | 'R' => {
                            rm.directory = true;
                            rm.recursive = true;
                        },
                        'v'=> rm.verbose = true,
                        _ => {
                            rm.stderr.write(b"invalid argument '").try(&mut rm.stderr);
                            rm.stderr.write(&[character as u8]).try(&mut rm.stderr);
                            rm.stderr.write(b"'\n").try(&mut rm.stderr);
                            rm.stderr.flush().try(&mut rm.stderr);
                        }
                    }
                }
            } else {
                if fs::metadata(&argument).is_err() {
                    rm.stderr.write(b"aborting due to invalid path: '").try(&mut rm.stderr);
                    rm.stderr.write(&argument.as_bytes()).try(&mut rm.stderr);
                    rm.stderr.write(b"'\n").try(&mut rm.stderr);
                    rm.stderr.flush().try(&mut rm.stderr);
                    exit(1);
                } else {
                    rm.arguments.push(PathBuf::from(&argument));
                }
            }
        }
        if rm.arguments.len() == 0 {
            rm.stdout.write(b"missing operand\nTry 'rm --help' for more information.\n").try(&mut rm.stderr);
            rm.stdout.flush().try(&mut rm.stderr);
            exit(0);
        }
        rm
    }

    /// Executes the rm program's arguments using any given flags.
    fn execute(&mut self) {
        let mut exit_status = 0i32;
        for argument in self.arguments.clone() {
            if argument.is_dir() {
                if self.interactive {
                    self.stdout.write(b"remove directory '").try(&mut self.stderr);
                    self.stdout.write(argument.to_string_lossy().as_bytes()).try(&mut self.stderr);
                    self.stdout.write(b"'? ").try(&mut self.stderr);
                    self.stdout.flush().try(&mut self.stderr);
                    let input = &mut String::new();
                    let stdin = io::stdin();
                    stdin.read_line(input).try(&mut self.stderr);
                    if input.chars().next().unwrap() != 'y' { continue }
                }
                if self.directory {
                    let status = self.remove_directory(&argument);
                    if exit_status == 0 { exit_status = status; }
                } else {
                    self.stderr.write(b"cannot remove '").try(&mut self.stderr);
                    self.stderr.write(argument.to_string_lossy().as_bytes()).try(&mut self.stderr);
                    self.stderr.write(b"': is a directory\n").try(&mut self.stderr);
                    self.stderr.flush().try(&mut self.stderr);
                    exit_status = 1;
                }
            } else {
                if self.interactive {
                    self.stdout.write(b"remove file '").try(&mut self.stderr);
                    self.stdout.write(argument.to_string_lossy().as_bytes()).try(&mut self.stderr);
                    self.stdout.write(b"'? ").try(&mut self.stderr);
                    self.stdout.flush().try(&mut self.stderr);
                    let input = &mut String::new();
                    let stdin = io::stdin();
                    stdin.read_line(input).try(&mut self.stderr);
                    if input.chars().next().unwrap() != 'y' { continue }
                }
                let status = self.remove_file(&argument);
                if exit_status == 0 { exit_status = status; }
            }
        }
        exit(exit_status);
    }

    /// Attempt to remove the file given as an input argument.
    fn remove_file(&mut self, file: &Path) -> i32 {
        if let Err(message) = fs::remove_file(file) {
            self.stderr.write(b"cannot remove '").try(&mut self.stderr);
            self.stderr.write(file.to_string_lossy().as_bytes()).try(&mut self.stderr);
            self.stderr.write(b"': ").try(&mut self.stderr);
            print_error(message, &mut self.stderr);
            return 1i32;
        } else if self.verbose {
            self.stdout.write(b"removed '").try(&mut self.stderr);
            self.stdout.write(file.to_string_lossy().as_bytes()).try(&mut self.stderr);
            self.stdout.write(b"'\n").try(&mut self.stderr);
            self.stdout.flush().try(&mut self.stderr);
        }
        return 0i32;
    }

    /// Attempt to remove a directory and all of it's contents if recursive mode is enabled.
    /// If recursion is not enabled, attempt to remove the directory if it is empty.
    fn remove_directory(&mut self, directory: &Path) -> i32 {
        // TODO: Use walkdir when it is implemented in Redox instead of fs::remove_dir_all().
        if self.recursive {
            if let Err(message) = fs::remove_dir_all(directory) {
                self.stderr.write(b"cannot remove directory '").try(&mut self.stderr);
                self.stderr.write(directory.to_string_lossy().as_bytes()).try(&mut self.stderr);
                self.stderr.write(b"': ").try(&mut self.stderr);
                print_error(message, &mut self.stderr);
                return 1i32;
            } else if self.verbose {
                self.stdout.write(b"removed directory '").try(&mut self.stderr);
                self.stdout.write(directory.to_string_lossy().as_bytes()).try(&mut self.stderr);
                self.stdout.write(b"'\n").try(&mut self.stderr);
                self.stdout.flush().try(&mut self.stderr);
            }
            return 0i32;
        } else {
            if let Err(message) = fs::remove_dir(directory) {
                self.stderr.write(b"cannot remove directory '").try(&mut self.stderr);
                self.stderr.write(directory.to_string_lossy().as_bytes()).try(&mut self.stderr);
                self.stderr.write(b"': ").try(&mut self.stderr);
                print_error(message, &mut self.stderr);
                return 1i32;
            } else if self.verbose {
                self.stdout.write(b"removed directory '").try(&mut self.stderr);
                self.stdout.write(directory.to_string_lossy().as_bytes()).try(&mut self.stderr);
                self.stdout.write(b"'\n").try(&mut self.stderr);
                self.stdout.flush().try(&mut self.stderr);
            }
            return 0i32;
        }
    }
}

/// Print the message given by an io::Error to stderr.
fn print_error(message: io::Error, stderr: &mut Stderr) {
    stderr.write(message.description().as_bytes()).try(stderr);
    stderr.write(b"\n").try(stderr);
    stderr.flush().try(stderr);
}
<|endoftext|>