<commit_before>//! # Parser Nodes
//!
//! Parser nodes represent the grammar that is defined
//! by the currently permissible set of commands and their
//! parameters.

use std::rc::Rc;

/// Minimum priority.
pub const PRIORITY_MINIMUM: i32 = -10000;
/// The default priority for a parameter.
pub const PRIORITY_PARAMETER: i32 = -10;
/// The default priority.
pub const PRIORITY_DEFAULT: i32 = 0;

/// A node in the tree of commands and their parameters
/// used by the `Parser`.
pub trait Node {
    /// The data describing this node.
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields;

    /// Nodes that are children of this node. Used to
    /// by the `Parser` during `advance`, `complete`, etc.
    fn successors(&self) -> &Vec<Rc<Node>> {
        &self.node_data().successors
    }

    /// The text used to identify this node in help text.
    /// This is typically the node name, either in plain
    /// form or decorated for parameters.
    fn help_symbol(&self) -> String {
        self.node_data().name.to_string()
    }

    /// Help text describing this node.
    fn help_text(&self) -> &Option<String> {
        unimplemented!();
    }

    /// Hidden nodes are still found for matching, but are
    /// hidden from completion.
    fn hidden(&self) -> bool {
        self.node_data().hidden
    }

    /// The name of this node.
    fn name(&self) -> &String {
        &self.node_data().name
    }

    /// This priority of this node during matching and completion.
    fn priority(&self) -> i32 {
        self.node_data().priority
    }
}

impl PartialEq for Node {
    /// Nodes are equal based on pointer equality.
    fn eq(&self, other: &Self) -> bool {
        self as *const _ == other as *const _
    }
}

/// A parse tree node.
#[doc(hidden)]
pub struct NodeFields {
    /// Possible successor nodes. Collected while building.
    successors: Vec<Rc<Node>>,
    /// The name of this node.
    name: String,
    /// Match and complete priority.
    priority: i32,
    /// Hidden nodes are not completed. This doesn't modify matching.
    hidden: bool,
}

/// The root of a command tree.
pub struct RootNode {
    node_fields: NodeFields,
}

impl Node for RootNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }
}

/// A node representing a command.
pub struct CommandNode {
    node_fields: NodeFields,
    command_fields: CommandNodeFields,
}

struct CommandNodeFields {
    help: Option<String>,
    handler: Option<fn(&node: Node) -> ()>,
    parameters: Vec<Rc<ParameterNode>>,
}

impl Node for CommandNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }

    fn help_text(&self) -> &Option<String> {
        &self.command_fields.help
    }
}

impl CommandNode {
    /// The handler which is executed once this node has been accepted.
    pub fn handler(&self) -> Option<fn(&node: Node) -> ()> {
        self.command_fields.handler
    }

    /// Get the parameter nodes for this command.
    pub fn parameters(&self) -> &Vec<Rc<ParameterNode>> {
        &self.command_fields.parameters
    }
}

/// A wrapper node wraps another command.
///
/// This is used for the help command so that it can complete
/// normal commands.
///
/// The `successors` will be those of the wrapped node.
pub struct WrapperNode {
    node_fields: NodeFields,
    #[allow(dead_code)]
    command_fields: CommandNodeFields,
    root: Rc<Node>,
}

impl Node for WrapperNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }

    fn successors(&self) -> &Vec<Rc<Node>> {
        self.root.successors()
    }
}

/// A repeatable node is an internal helper for representing
/// nodes that can be repeated, like some parameters.
pub trait RepeatableNode: Node {
    /// Internal data for a repeatable node.
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields;

    /// Whether or not this node can be repeated. A repeated
    /// node can be `accept`ed multiple times.
    fn repeatable(&self) -> bool {
        self.repeatable_data().repeatable
    }

    /// If present, this node will no longer be `acceptable`.
    fn repeat_marker(&self) -> &Option<Rc<Node>> {
        &self.repeatable_data().repeat_marker
    }
}

/// The data for a repeatable node.
#[doc(hidden)]
pub struct RepeatableNodeFields {
    repeatable: bool,
    repeat_marker: Option<Rc<Node>>,
}

/// A node that represented the name portion of a named
/// parameter.
pub struct ParameterNameNode {
    node_fields: NodeFields,
    repeatable_fields: RepeatableNodeFields,
    help: Option<String>,
    parameter: Rc<Node>,
}

impl Node for ParameterNameNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }

    fn help_symbol(&self) -> String {
        self.node_fields.name.clone() + " " + self.parameter.help_symbol().as_str()
    }

    fn help_text(&self) -> &Option<String> {
        &self.help
    }
}

impl RepeatableNode for ParameterNameNode {
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields {
        &self.repeatable_fields
    }
}

/// Parameter nodes.
pub trait ParameterNode {
    /// Internal data for a parameter node.
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields;

    /// A `required` parameter must be supplied for the
    /// command line being parsed to be valid.
    fn required(&self) -> bool {
        self.parameter_data().required
    }
}

impl RepeatableNode for ParameterNode {
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields {
        &self.parameter_data().repeatable
    }
}

/// Data for parameter nodes.
#[doc(hidden)]
pub struct ParameterNodeFields {
    node: NodeFields,
    repeatable: RepeatableNodeFields,
    help: Option<String>,
    required: bool,
}

impl Node for ParameterNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.parameter_data().node
    }

    fn help_symbol(&self) -> String {
        String::from("<") + self.node_data().name.as_str() +
        if self.repeatable() {
            ">..."
        } else {
            ">"
        }
    }

    fn help_text(&self) -> &Option<String> {
        &self.parameter_data().help
    }
}

/// A flag parameter node.
///
/// When implemented, this will only have a value of
/// true when it is present.
pub struct FlagParameterNode {
    parameter_fields: ParameterNodeFields,
}

impl ParameterNode for FlagParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.parameter_fields
    }
}

/// A named parameter node.
pub struct NamedParameterNode {
    parameter_fields: ParameterNodeFields,
}

impl ParameterNode for NamedParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.parameter_fields
    }
}

/// A simple parameter node. This is only present in a command
/// line as a value.
pub struct SimpleParameterNode {
    parameter_fields: ParameterNodeFields,
}

impl ParameterNode for SimpleParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.parameter_fields
    }
}
<commit_msg>Nodes: RootNode::new, CommandNode::new.<commit_after>//! # Parser Nodes
//!
//! Parser nodes represent the grammar that is defined
//! by the currently permissible set of commands and their
//! parameters.

use std::rc::Rc;

/// Minimum priority.
pub const PRIORITY_MINIMUM: i32 = -10000;
/// The default priority for a parameter.
pub const PRIORITY_PARAMETER: i32 = -10;
/// The default priority.
pub const PRIORITY_DEFAULT: i32 = 0;

/// A node in the tree of commands and their parameters
/// used by the `Parser`.
pub trait Node {
    /// The data describing this node.
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields;

    /// Nodes that are children of this node. Used to
    /// by the `Parser` during `advance`, `complete`, etc.
    fn successors(&self) -> &Vec<Rc<Node>> {
        &self.node_data().successors
    }

    /// The text used to identify this node in help text.
    /// This is typically the node name, either in plain
    /// form or decorated for parameters.
    fn help_symbol(&self) -> String {
        self.node_data().name.to_string()
    }

    /// Help text describing this node.
    fn help_text(&self) -> &Option<String> {
        unimplemented!();
    }

    /// Hidden nodes are still found for matching, but are
    /// hidden from completion.
    fn hidden(&self) -> bool {
        self.node_data().hidden
    }

    /// The name of this node.
    fn name(&self) -> &String {
        &self.node_data().name
    }

    /// This priority of this node during matching and completion.
    fn priority(&self) -> i32 {
        self.node_data().priority
    }
}

impl PartialEq for Node {
    /// Nodes are equal based on pointer equality.
    fn eq(&self, other: &Self) -> bool {
        self as *const _ == other as *const _
    }
}

/// A parse tree node.
#[doc(hidden)]
pub struct NodeFields {
    /// Possible successor nodes. Collected while building.
    successors: Vec<Rc<Node>>,
    /// The name of this node.
    name: String,
    /// Match and complete priority.
    priority: i32,
    /// Hidden nodes are not completed. This doesn't modify matching.
    hidden: bool,
}

/// The root of a command tree.
///
/// ```
/// use commands::parser::nodes::RootNode;
///
/// let root = RootNode::new();
/// ```
pub struct RootNode {
    node_fields: NodeFields,
}

impl RootNode {
    /// Create a new `RootNode`
    pub fn new() -> Rc<Self> {
        Rc::new(RootNode {
            node_fields: NodeFields {
                successors: vec![],
                name: "__root__".to_string(),
                priority: PRIORITY_DEFAULT,
                hidden: false,
            },
        })
    }
}

impl Node for RootNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }
}

/// A node representing a command.
pub struct CommandNode {
    node_fields: NodeFields,
    command_fields: CommandNodeFields,
}

struct CommandNodeFields {
    help: Option<String>,
    handler: Option<fn(&node: Node) -> ()>,
    parameters: Vec<Rc<ParameterNode>>,
}

impl CommandNode {
    /// Construct a new `CommandNode`.
    pub fn new(name: &str,
               priority: i32,
               hidden: bool,
               help: Option<String>,
               handler: Option<fn(&node: Node) -> ()>)
               -> Rc<Self> {
        Rc::new(CommandNode {
            node_fields: NodeFields {
                successors: vec![],
                name: name.to_string(),
                priority: priority,
                hidden: hidden,
            },
            command_fields: CommandNodeFields {
                help: help,
                handler: handler,
                parameters: vec![],
            },
        })
    }
}
impl Node for CommandNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }

    fn help_text(&self) -> &Option<String> {
        &self.command_fields.help
    }
}

impl CommandNode {
    /// The handler which is executed once this node has been accepted.
    pub fn handler(&self) -> Option<fn(&node: Node) -> ()> {
        self.command_fields.handler
    }

    /// Get the parameter nodes for this command.
    pub fn parameters(&self) -> &Vec<Rc<ParameterNode>> {
        &self.command_fields.parameters
    }
}

/// A wrapper node wraps another command.
///
/// This is used for the help command so that it can complete
/// normal commands.
///
/// The `successors` will be those of the wrapped node.
pub struct WrapperNode {
    node_fields: NodeFields,
    #[allow(dead_code)]
    command_fields: CommandNodeFields,
    root: Rc<Node>,
}

impl Node for WrapperNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }

    fn successors(&self) -> &Vec<Rc<Node>> {
        self.root.successors()
    }
}

/// A repeatable node is an internal helper for representing
/// nodes that can be repeated, like some parameters.
pub trait RepeatableNode: Node {
    /// Internal data for a repeatable node.
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields;

    /// Whether or not this node can be repeated. A repeated
    /// node can be `accept`ed multiple times.
    fn repeatable(&self) -> bool {
        self.repeatable_data().repeatable
    }

    /// If present, this node will no longer be `acceptable`.
    fn repeat_marker(&self) -> &Option<Rc<Node>> {
        &self.repeatable_data().repeat_marker
    }
}

/// The data for a repeatable node.
#[doc(hidden)]
pub struct RepeatableNodeFields {
    repeatable: bool,
    repeat_marker: Option<Rc<Node>>,
}

/// A node that represented the name portion of a named
/// parameter.
pub struct ParameterNameNode {
    node_fields: NodeFields,
    repeatable_fields: RepeatableNodeFields,
    help: Option<String>,
    parameter: Rc<Node>,
}

impl Node for ParameterNameNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }

    fn help_symbol(&self) -> String {
        self.node_fields.name.clone() + " " + self.parameter.help_symbol().as_str()
    }

    fn help_text(&self) -> &Option<String> {
        &self.help
    }
}

impl RepeatableNode for ParameterNameNode {
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields {
        &self.repeatable_fields
    }
}

/// Parameter nodes.
pub trait ParameterNode {
    /// Internal data for a parameter node.
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields;

    /// A `required` parameter must be supplied for the
    /// command line being parsed to be valid.
    fn required(&self) -> bool {
        self.parameter_data().required
    }
}

impl RepeatableNode for ParameterNode {
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields {
        &self.parameter_data().repeatable
    }
}

/// Data for parameter nodes.
#[doc(hidden)]
pub struct ParameterNodeFields {
    node: NodeFields,
    repeatable: RepeatableNodeFields,
    help: Option<String>,
    required: bool,
}

impl Node for ParameterNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.parameter_data().node
    }

    fn help_symbol(&self) -> String {
        String::from("<") + self.node_data().name.as_str() +
        if self.repeatable() {
            ">..."
        } else {
            ">"
        }
    }

    fn help_text(&self) -> &Option<String> {
        &self.parameter_data().help
    }
}

/// A flag parameter node.
///
/// When implemented, this will only have a value of
/// true when it is present.
pub struct FlagParameterNode {
    parameter_fields: ParameterNodeFields,
}

impl ParameterNode for FlagParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.parameter_fields
    }
}

/// A named parameter node.
pub struct NamedParameterNode {
    parameter_fields: ParameterNodeFields,
}

impl ParameterNode for NamedParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.parameter_fields
    }
}

/// A simple parameter node. This is only present in a command
/// line as a value.
pub struct SimpleParameterNode {
    parameter_fields: ParameterNodeFields,
}

impl ParameterNode for SimpleParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.parameter_fields
    }
}
<|endoftext|>


<commit_before>use postgres::{Connection, SslMode};
use postgres::error::ConnectError;
use std::collections::BTreeMap;
use std::env;
use rustc_serialize::json::{ToJson, Json};
use uuid::Uuid;

static DEFAULT_DATABASE_URL: &'static str = "postgres://kumabook@localhost/pink_spider_development_master";

use youtube;
use soundcloud;

#[derive(Debug, Clone)]
pub enum Provider {
    YouTube,
    SoundCloud,
    Raw
}
impl PartialEq for Provider {
    fn eq(&self, p: &Provider) -> bool {
        match *self {
            Provider::YouTube    => match *p { Provider::YouTube    => true, _ => false },
            Provider::SoundCloud => match *p { Provider::SoundCloud => true, _ => false },
            Provider::Raw        => match *p { Provider::Raw        => true, _ => false }
        }
    }
}
impl Provider {
    fn to_string(&self) -> String {
        match *self {
            Provider::YouTube    => "YouTube".to_string(),
            Provider::SoundCloud => "SoundCloud".to_string(),
            Provider::Raw        => "Raw".to_string(),
        }
    }
    fn new(str: String) -> Provider {
        match str.as_ref() {
            "YouTube"    => Provider::YouTube,
            "SoundCloud" => Provider::SoundCloud,
            _            => Provider::Raw,
        }
    }
}

#[derive(Debug, Clone)]
pub struct Track {
    pub id:         Uuid,
    pub provider:   Provider,
    pub title:      String,
    pub url:        String,
    pub identifier: String
}

impl PartialEq for Track {
    fn eq(&self, t: &Track) -> bool {
        return self.identifier == t.identifier && self.provider == t.provider
    }
}

impl ToJson for Track {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("id".to_string(),         self.id.to_string().to_json());
        d.insert("provider".to_string(),   self.provider.to_string().to_json());
        d.insert("identifier".to_string(), self.identifier.to_json());
        d.insert("title".to_string(),      self.title.to_json());
        d.insert("url".to_string(),        self.url.to_json());
        Json::Object(d)
    }
}

impl Track {
    pub fn from_yt_playlist_item(item: &youtube::PlaylistItem) -> Track {
        let identifier = (*item).snippet.resourceId["videoId"].to_string();
        Track {
                    id: Uuid::new_v4(),
              provider: Provider::YouTube,
                 title: (*item).snippet.title.to_string(),
                   url: format!("https://www.youtube.com/watch/?v={}", identifier),
            identifier: identifier
        }
    }
    pub fn from_sc_track(track: &soundcloud::Track) -> Track {
        Track {
                    id: Uuid::new_v4(),
              provider: Provider::SoundCloud,
                 title: (*track).title.to_string(),
                   url: (*track).permalink_url.to_string(),
            identifier: (*track).id.to_string()
        }
    }
    pub fn find_by_id(id: i32) -> Option<Track> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, provider, title, url, identifier
                                 FROM tracks WHERE id = $1").unwrap();
        for row in stmt.query(&[&id]).unwrap().iter() {
            let track = Track {
                      id: row.get(0),
                provider: Provider::new(row.get(1)),
                   title: row.get(2),
                     url: row.get(3),
              identifier: row.get(4)
            };
            return Some(track);
        }
        return None
    }

    pub fn find_by(provider: &Provider, identifier: &str) -> Option<Track> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id,  provider, title, url, identifier
                                 FROM tracks WHERE provider = $1
                                 AND identifier = $2").unwrap();
        for row in stmt.query(&[&(*provider).to_string(), &identifier]).unwrap().iter() {
            let track = Track {
                      id: row.get(0),
                provider: Provider::new(row.get(1)),
                   title: row.get(2),
                     url: row.get(3),
              identifier: row.get(4)
            };
            return Some(track);
        }
        return None
    }

    pub fn find_by_entry_id(entry_id: Uuid) -> Vec<Track> {
        let mut tracks = Vec::new();
        let conn = conn().unwrap();
        println!(" entry_id {}", entry_id);
        let stmt = conn.prepare("SELECT t.id,
                                        t.provider,
                                        t.title,
                                        t.url,
                                        t.identifier
                                 FROM tracks t LEFT JOIN track_entries te
                                 ON t.id = te.track_id
                                 WHERE te.entry_id = $1").unwrap();
        for row in stmt.query(&[&entry_id]).unwrap().iter() {
            tracks.push(Track {
                      id: row.get(0),
                provider: Provider::new(row.get(1)),
                   title: row.get(2),
                     url: row.get(3),
              identifier: row.get(4)
            });
        }
        return tracks
    }

    pub fn find_all() -> Vec<Track> {
        let mut tracks = Vec::new();
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, provider, title, url, identifier FROM track").unwrap();
        for row in stmt.query(&[]).unwrap().iter() {
            let track = Track {
                      id: row.get(0),
                provider: Provider::new(row.get(1)),
                   title: row.get(2),
                     url: row.get(3),
              identifier: row.get(4)
            };
            tracks.push(track);
        }
        return tracks
    }

    pub fn create(provider: Provider, title: String, url: String, identifier: String) -> Option<Track> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("INSERT INTO tracks (provider, title, url, identifier)
                                 VALUES ($1, $2, $3, $4) RETURNING id").unwrap();
        for row in stmt.query(&[&provider.to_string(), &title, &url, &identifier]).unwrap().iter() {
            let track = Track {
                        id: row.get(0),
                  provider: provider,
                     title: title,
                       url: url,
                identifier: identifier
            };
            return Some(track);
        }
        return None
    }

    pub fn find_or_create(provider: Provider, title: String, url: String, identifier: String) -> Option<Track> {
        return match Track::find_by(&provider, &identifier) {
            Some(track) => Some(track),
            None        => Track::create(provider, title, url, identifier)
        }
    }

    pub fn save(&self) -> bool {
        let conn = conn().unwrap();
        let stmt = conn.prepare("UPDATE track SET title=$1, url=$2 WHERE id = $3").unwrap();
        let result = stmt.query(&[&self.title, &self.url, &self.id.to_string()]);
        match result {
            Ok(_)  => return true,
            Err(_) => return false
        }
    }
}

pub struct Playlist {
    pub title:  String,
    pub tracks: Vec<Track>,
}

#[derive(Debug)]
pub struct Entry {
    pub id:  Uuid,
    pub url: String,
    pub tracks: Vec<Track>,
}

impl ToJson for Entry {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("id".to_string(),  self.id.to_string().to_json());
        d.insert("url".to_string(), self.url.to_json());
        let ref tracks = self.tracks;
        let mut t = Vec::new();
        for ref x in tracks.iter() {
            t.push(x.to_json());
        }
        d.insert("tracks".to_string(), Json::Array(t));
        Json::Object(d)
    }
}

impl Entry {
    pub fn find_by_id(id: String) -> Option<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, url FROM entries WHERE id = $1").unwrap();
        for row in stmt.query(&[&id]).unwrap().iter() {
            return Some(Entry {
                    id: row.get(0),
                   url: row.get(1),
                tracks: Track::find_by_entry_id(row.get(0))
            });
        }
        return None
    }

    pub fn find_by_url(url: &str) -> Option<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, url FROM entries WHERE url = $1").unwrap();
        for row in stmt.query(&[&url]).unwrap().iter() {
            return Some(Entry {
                    id: row.get(0),
                   url: row.get(1),
                tracks: Track::find_by_entry_id(row.get(0))
            });
        }
        return None
    }

    pub fn find_all() -> Vec<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, url FROM entries").unwrap();
        let mut entries = Vec::new();
        for row in stmt.query(&[]).unwrap().iter() {
            entries.push(Entry {
                    id: row.get(0),
                   url: row.get(1),
                tracks: Track::find_by_entry_id(row.get(0))
            })
        }
        return entries
    }

    pub fn find_or_create_by_url(url: String) -> Option<Entry> {
        return match Entry::find_by_url(&url) {
            Some(entry) => Some(entry),
            None        => Entry::create_by_url(url)
        }
    }

    pub fn create_by_url(url: String) -> Option<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("INSERT INTO entries (url) VALUES ($1) RETURNING id").unwrap();
        for row in stmt.query(&[&url]).unwrap().iter() {
            let entry = Entry {
                      id: row.get(0),
                     url: url,
                  tracks: Vec::new()
            };
            return Some(entry);
        }
        return None
    }

    pub fn add_track(&mut self, track: Track) {
        let conn = conn().unwrap();
        let stmt = conn.prepare("INSERT INTO track_entries (track_id, entry_id)
                                 VALUES ($1, $2)").unwrap();
        stmt.query(&[&track.id, &self.id]).unwrap();
        self.tracks.push(track);
    }

    pub fn save(&self) -> bool {
        return true
    }
}

pub fn conn() -> Result<Connection, ConnectError> {
    let opt_url = env::var("DATABASE_URL");
    match opt_url {
        Ok(url) =>
            Connection::connect(url.trim(), SslMode::None),
        Err(_)  =>
            Connection::connect(DEFAULT_DATABASE_URL, SslMode::None)
    }
}
<commit_msg>Impl fm::Display for Provider and Track<commit_after>use postgres::{Connection, SslMode};
use postgres::error::ConnectError;
use std::collections::BTreeMap;
use std::env;
use rustc_serialize::json::{ToJson, Json};
use uuid::Uuid;
use std::fmt;

static DEFAULT_DATABASE_URL: &'static str = "postgres://kumabook@localhost/pink_spider_development_master";

use youtube;
use soundcloud;

#[derive(Debug, Clone)]
pub enum Provider {
    YouTube,
    SoundCloud,
    Raw
}
impl PartialEq for Provider {
    fn eq(&self, p: &Provider) -> bool {
        match *self {
            Provider::YouTube    => match *p { Provider::YouTube    => true, _ => false },
            Provider::SoundCloud => match *p { Provider::SoundCloud => true, _ => false },
            Provider::Raw        => match *p { Provider::Raw        => true, _ => false }
        }
    }
}
impl Provider {
    fn to_string(&self) -> String {
        match *self {
            Provider::YouTube    => "YouTube".to_string(),
            Provider::SoundCloud => "SoundCloud".to_string(),
            Provider::Raw        => "Raw".to_string(),
        }
    }
    fn new(str: String) -> Provider {
        match str.as_ref() {
            "YouTube"    => Provider::YouTube,
            "SoundCloud" => Provider::SoundCloud,
            _            => Provider::Raw,
        }
    }
}

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

#[derive(Debug, Clone)]
pub struct Track {
    pub id:         Uuid,
    pub provider:   Provider,
    pub title:      String,
    pub url:        String,
    pub identifier: String
}

impl PartialEq for Track {
    fn eq(&self, t: &Track) -> bool {
        return self.identifier == t.identifier && self.provider == t.provider
    }
}

impl ToJson for Track {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("id".to_string(),         self.id.to_string().to_json());
        d.insert("provider".to_string(),   self.provider.to_string().to_json());
        d.insert("identifier".to_string(), self.identifier.to_json());
        d.insert("title".to_string(),      self.title.to_json());
        d.insert("url".to_string(),        self.url.to_json());
        Json::Object(d)
    }
}

impl fmt::Display for Track {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.provider, self.identifier)
    }
}

impl Track {
    pub fn from_yt_playlist_item(item: &youtube::PlaylistItem) -> Track {
        let identifier = (*item).snippet.resourceId["videoId"].to_string();
        Track {
                    id: Uuid::new_v4(),
              provider: Provider::YouTube,
                 title: (*item).snippet.title.to_string(),
                   url: format!("https://www.youtube.com/watch/?v={}", identifier),
            identifier: identifier
        }
    }
    pub fn from_sc_track(track: &soundcloud::Track) -> Track {
        Track {
                    id: Uuid::new_v4(),
              provider: Provider::SoundCloud,
                 title: (*track).title.to_string(),
                   url: (*track).permalink_url.to_string(),
            identifier: (*track).id.to_string()
        }
    }
    pub fn find_by_id(id: i32) -> Option<Track> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, provider, title, url, identifier
                                 FROM tracks WHERE id = $1").unwrap();
        for row in stmt.query(&[&id]).unwrap().iter() {
            let track = Track {
                      id: row.get(0),
                provider: Provider::new(row.get(1)),
                   title: row.get(2),
                     url: row.get(3),
              identifier: row.get(4)
            };
            return Some(track);
        }
        return None
    }

    pub fn find_by(provider: &Provider, identifier: &str) -> Option<Track> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id,  provider, title, url, identifier
                                 FROM tracks WHERE provider = $1
                                 AND identifier = $2").unwrap();
        for row in stmt.query(&[&(*provider).to_string(), &identifier]).unwrap().iter() {
            let track = Track {
                      id: row.get(0),
                provider: Provider::new(row.get(1)),
                   title: row.get(2),
                     url: row.get(3),
              identifier: row.get(4)
            };
            return Some(track);
        }
        return None
    }

    pub fn find_by_entry_id(entry_id: Uuid) -> Vec<Track> {
        let mut tracks = Vec::new();
        let conn = conn().unwrap();
        println!(" entry_id {}", entry_id);
        let stmt = conn.prepare("SELECT t.id,
                                        t.provider,
                                        t.title,
                                        t.url,
                                        t.identifier
                                 FROM tracks t LEFT JOIN track_entries te
                                 ON t.id = te.track_id
                                 WHERE te.entry_id = $1").unwrap();
        for row in stmt.query(&[&entry_id]).unwrap().iter() {
            tracks.push(Track {
                      id: row.get(0),
                provider: Provider::new(row.get(1)),
                   title: row.get(2),
                     url: row.get(3),
              identifier: row.get(4)
            });
        }
        return tracks
    }

    pub fn find_all() -> Vec<Track> {
        let mut tracks = Vec::new();
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, provider, title, url, identifier FROM track").unwrap();
        for row in stmt.query(&[]).unwrap().iter() {
            let track = Track {
                      id: row.get(0),
                provider: Provider::new(row.get(1)),
                   title: row.get(2),
                     url: row.get(3),
              identifier: row.get(4)
            };
            tracks.push(track);
        }
        return tracks
    }

    pub fn create(provider: Provider, title: String, url: String, identifier: String) -> Option<Track> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("INSERT INTO tracks (provider, title, url, identifier)
                                 VALUES ($1, $2, $3, $4) RETURNING id").unwrap();
        for row in stmt.query(&[&provider.to_string(), &title, &url, &identifier]).unwrap().iter() {
            let track = Track {
                        id: row.get(0),
                  provider: provider,
                     title: title,
                       url: url,
                identifier: identifier
            };
            return Some(track);
        }
        return None
    }

    pub fn find_or_create(provider: Provider, title: String, url: String, identifier: String) -> Option<Track> {
        return match Track::find_by(&provider, &identifier) {
            Some(track) => Some(track),
            None        => Track::create(provider, title, url, identifier)
        }
    }

    pub fn save(&self) -> bool {
        let conn = conn().unwrap();
        let stmt = conn.prepare("UPDATE track SET title=$1, url=$2 WHERE id = $3").unwrap();
        let result = stmt.query(&[&self.title, &self.url, &self.id.to_string()]);
        match result {
            Ok(_)  => return true,
            Err(_) => return false
        }
    }
}

pub struct Playlist {
    pub title:  String,
    pub tracks: Vec<Track>,
}

#[derive(Debug)]
pub struct Entry {
    pub id:  Uuid,
    pub url: String,
    pub tracks: Vec<Track>,
}

impl ToJson for Entry {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("id".to_string(),  self.id.to_string().to_json());
        d.insert("url".to_string(), self.url.to_json());
        let ref tracks = self.tracks;
        let mut t = Vec::new();
        for ref x in tracks.iter() {
            t.push(x.to_json());
        }
        d.insert("tracks".to_string(), Json::Array(t));
        Json::Object(d)
    }
}

impl Entry {
    pub fn find_by_id(id: String) -> Option<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, url FROM entries WHERE id = $1").unwrap();
        for row in stmt.query(&[&id]).unwrap().iter() {
            return Some(Entry {
                    id: row.get(0),
                   url: row.get(1),
                tracks: Track::find_by_entry_id(row.get(0))
            });
        }
        return None
    }

    pub fn find_by_url(url: &str) -> Option<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, url FROM entries WHERE url = $1").unwrap();
        for row in stmt.query(&[&url]).unwrap().iter() {
            return Some(Entry {
                    id: row.get(0),
                   url: row.get(1),
                tracks: Track::find_by_entry_id(row.get(0))
            });
        }
        return None
    }

    pub fn find_all() -> Vec<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, url FROM entries").unwrap();
        let mut entries = Vec::new();
        for row in stmt.query(&[]).unwrap().iter() {
            entries.push(Entry {
                    id: row.get(0),
                   url: row.get(1),
                tracks: Track::find_by_entry_id(row.get(0))
            })
        }
        return entries
    }

    pub fn find_or_create_by_url(url: String) -> Option<Entry> {
        return match Entry::find_by_url(&url) {
            Some(entry) => Some(entry),
            None        => Entry::create_by_url(url)
        }
    }

    pub fn create_by_url(url: String) -> Option<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("INSERT INTO entries (url) VALUES ($1) RETURNING id").unwrap();
        for row in stmt.query(&[&url]).unwrap().iter() {
            let entry = Entry {
                      id: row.get(0),
                     url: url,
                  tracks: Vec::new()
            };
            return Some(entry);
        }
        return None
    }

    pub fn add_track(&mut self, track: Track) {
        let conn = conn().unwrap();
        let stmt = conn.prepare("INSERT INTO track_entries (track_id, entry_id)
                                 VALUES ($1, $2)").unwrap();
        stmt.query(&[&track.id, &self.id]).unwrap();
        self.tracks.push(track);
    }

    pub fn save(&self) -> bool {
        return true
    }
}

pub fn conn() -> Result<Connection, ConnectError> {
    let opt_url = env::var("DATABASE_URL");
    match opt_url {
        Ok(url) =>
            Connection::connect(url.trim(), SslMode::None),
        Err(_)  =>
            Connection::connect(DEFAULT_DATABASE_URL, SslMode::None)
    }
}
<|endoftext|>


<commit_before>use http::server::request::{AbsolutePath};
use regex::Regex;

use iron::{Middleware, Request, Response, Alloy, Chain};
use iron::middleware::{Status, Continue};

/// `Mount` is a simple mounting middleware.
///
/// `Mount` allows you to mount other middleware within a larger chain.
///
/// Mounted middleware will receive a modified version of the request
/// url with the mount pattern removed from the start of the url.
pub struct Mount {
    route: String,
    matches: Regex,
    middleware: Box<Middleware + Send>
}

impl Clone for Mount {
    fn clone(&self) -> Mount {
        Mount {
            route: self.route.clone(),
            matches: self.matches.clone(),
            middleware: self.middleware.clone_box()
        }
    }
}

impl Mount {
    /// Creates a new instance of `Mount` mounting the given instance of Iron
    /// on the given path.
    pub fn new<M: Middleware + Send>(route: &str, middleware: M) -> Mount {
        Mount {
            route: route.to_string(),
            middleware: box middleware,
            matches: to_regex(route)
        }
    }
}

fn to_regex(route: &str) -> Regex {
    Regex::new("^".to_string().append(route).as_slice()).unwrap()
}

impl Middleware for Mount {
    fn enter(&mut self,
             req: &mut Request,
             res: &mut Response,
             alloy: &mut Alloy) -> Status {
        // This method is ugly, but it is hard to make it pretty
        // because we can't both borrow path from inside of request
        // while allowing chain.dispatch to borrow it as mutable.

        match req.request_uri {
           AbsolutePath(ref path) => {
               // Short circuit if we don't match.
                if !self.matches.is_match(path.as_slice()) {
                    return Continue;
                }
           },
           // Short circuit if this is not an AbsolutePath.
           _ => { return Continue; }
        }

        // We are a match, so fire off to our child instance.
        match req.request_uri {
            AbsolutePath(ref mut path) => {
                *path = path.as_slice().slice_from(self.route.len()).to_string();
                if path.is_empty() {
                    *path = "/".to_string();
                }
            },
            // Absolutely cannot happen because of our previous check,
            // but this is here just to be careful.
            _ => { return Continue; }
        } // Previous borrow of req ends here.

        // So we can borrow it again here.
        let terminator = self.middleware.enter(req, res, alloy);
        let _ = self.middleware.exit(req, res, alloy);

        // And repair the damage here, for future middleware
        match req.request_uri {
            AbsolutePath(ref mut path) => {
                *path = self.route.clone().append(path.as_slice());
            },
            // This really, really should never happen.
            _ => { fail!("The impossible happened."); }
        }

        // We dispatched the request, so return the terminator.
        terminator
    }
}

<commit_msg>(fix) Fixes #15, remove special case handling.<commit_after>use http::server::request::{AbsolutePath};
use regex::Regex;

use iron::{Middleware, Request, Response, Alloy, Chain};
use iron::middleware::{Status, Continue};

/// `Mount` is a simple mounting middleware.
///
/// `Mount` allows you to mount other middleware within a larger chain.
///
/// Mounted middleware will receive a modified version of the request
/// url with the mount pattern removed from the start of the url.
pub struct Mount {
    route: String,
    matches: Regex,
    middleware: Box<Middleware + Send>
}

impl Clone for Mount {
    fn clone(&self) -> Mount {
        Mount {
            route: self.route.clone(),
            matches: self.matches.clone(),
            middleware: self.middleware.clone_box()
        }
    }
}

impl Mount {
    /// Creates a new instance of `Mount` mounting the given instance of Iron
    /// on the given path.
    pub fn new<M: Middleware + Send>(route: &str, middleware: M) -> Mount {
        Mount {
            route: route.to_string(),
            middleware: box middleware,
            matches: to_regex(route)
        }
    }
}

fn to_regex(route: &str) -> Regex {
    Regex::new("^".to_string().append(route).as_slice()).unwrap()
}

impl Middleware for Mount {
    fn enter(&mut self,
             req: &mut Request,
             res: &mut Response,
             alloy: &mut Alloy) -> Status {
        // This method is ugly, but it is hard to make it pretty
        // because we can't both borrow path from inside of request
        // while allowing chain.dispatch to borrow it as mutable.

        match req.request_uri {
           AbsolutePath(ref path) => {
               // Short circuit if we don't match.
                if !self.matches.is_match(path.as_slice()) {
                    return Continue;
                }
           },
           // Short circuit if this is not an AbsolutePath.
           _ => { return Continue; }
        }

        // We are a match, so fire off to our child instance.
        match req.request_uri {
            AbsolutePath(ref mut path) => {
                *path = path.as_slice().slice_from(self.route.len()).to_string();
            },
            // Absolutely cannot happen because of our previous check,
            // but this is here just to be careful.
            _ => { return Continue; }
        } // Previous borrow of req ends here.

        // So we can borrow it again here.
        let terminator = self.middleware.enter(req, res, alloy);
        let _ = self.middleware.exit(req, res, alloy);

        // And repair the damage here, for future middleware
        match req.request_uri {
            AbsolutePath(ref mut path) => {
                *path = self.route.clone().append(path.as_slice());
            },
            // This really, really should never happen.
            _ => { fail!("The impossible happened."); }
        }

        // We dispatched the request, so return the terminator.
        terminator
    }
}

<|endoftext|>


<commit_before>//! The parse module is responsible for parsing events published by clients.

use regex::Regex;

use std::str::{self, FromStr};

use error::{Error, CapellaResult};

/// MetricType defines what kind of metric was parsed from a client.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum MetricType {
    Counter,
    Gauge,
    Histogram,
    Meter,
    Timer,
}

impl FromStr for MetricType {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "c" => Ok(MetricType::Counter),
            "g" => Ok(MetricType::Gauge),
            "h" => Ok(MetricType::Histogram),
            "m" => Ok(MetricType::Meter),
            "ms" => Ok(MetricType::Timer),
            _ => Err(Error::Parse),
        }
    }
}

/// A Metric defines a published client event.
#[derive(Debug, PartialEq)]
pub struct Metric {
    pub name: String,
    pub value: f64,
    pub metric_type: MetricType,
    pub sample_rate: Option<f64>,
}

impl Metric {
    pub fn new() -> Metric {
        Metric {
            name: String::new(),
            value: 0.0,
            metric_type: MetricType::Meter,
            sample_rate: None,
        }
    }
}

/// The parse_metric function trys to break down a single UDP packet into a single metric.
pub fn parse_metric(packet: &[u8]) -> CapellaResult<Metric> {
    lazy_static! {
        static ref PATTERN: Regex = Regex::new(r"(?x)
            \A(?P<name>[\w\.]+):
            ((?P<sign>\-|\+))?
            (?P<val>([0-9]*[.])?[0-9]+)
            \|(?P<type>\w+)
            (\|@(?P<rate>\d+\.\d+))?\z").unwrap();
    }

    if let Ok(val) = str::from_utf8(packet) {
        if !PATTERN.is_match(val) {
            return Err(Error::Parse);
        }
    } else {
        return Err(Error::Parse);
    }

    // We know this matched before so it is safe to unwrap.
    let caps = PATTERN.captures(str::from_utf8(packet).unwrap()).unwrap();

    let mut metric = Metric::new();
    // These are required to match.
    let name = caps.name("name").unwrap().as_str();
    let value = caps.name("val").unwrap().as_str().parse::<f64>().map_err(Error::from)?;
    let metric_type = caps.name("type").unwrap().as_str();

    metric.name = String::from(name);
    metric.value = value;
    metric.metric_type = metric_type.parse::<MetricType>()?;

    // Now see if there were optional values added in.
    if let Some(sign) = caps.name("sign") {
        let s = sign.as_str();
        if s == "-" {
            metric.value *= -1.0;
        }
    }

    if let Some(rate) = caps.name("rate") {
        let r = rate.as_str().parse::<f64>().map_err(Error::from)?;
        metric.sample_rate = Some(r);
    }

    Ok(metric)
}

#[cfg(test)]
mod tests {
    use super::{Metric, MetricType, parse_metric};

    #[test]
    fn good_simple_meter() {
        let packet = b"test:1|m";
        let m1 = parse_metric(packet).unwrap();

        let mut m2 = Metric::new();
        m2.name = String::from("test");
        m2.value = 1.0;

        assert_eq!(m1, m2);
    }

    #[test]
    fn bad_parse_double_colon() {
        let packet = b"test::1|c";
        assert!(parse_metric(packet).is_err());
    }

    #[test]
    fn good_timing_with_rate() {
        let packet = b"test:1|ms|@0.1";
        let m1 = parse_metric(packet).unwrap();

        let mut m2 = Metric::new();
        m2.name = String::from("test");
        m2.value = 1.0;
        m2.metric_type = MetricType::Timer;
        m2.sample_rate = Some(0.1);

        assert_eq!(m1, m2);
    }

    #[test]
    fn good_parse_float_value() {
        let packet = b"test:1.0|g";
        assert!(parse_metric(packet).is_ok());
    }

    #[test]
    fn bad_parse_unknown_metric_type() {
        let packet = b"test:1|a";
        assert!(parse_metric(packet).is_err());
    }

    #[test]
    fn good_nested_metric_name() {
        let packet = b"test.nested.name:1|c";
        let m1 = parse_metric(packet).unwrap();

        let mut m2 = Metric::new();
        m2.name = String::from("test.nested.name");
        m2.value = 1.0;
        m2.metric_type = MetricType::Counter;

        assert_eq!(m1, m2);
    }
}
<commit_msg>use table-driven testing for negative cases<commit_after>//! The parse module is responsible for parsing events published by clients.

use regex::Regex;

use std::str::{self, FromStr};

use error::{Error, CapellaResult};

/// MetricType defines what kind of metric was parsed from a client.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum MetricType {
    Counter,
    Gauge,
    Histogram,
    Meter,
    Timer,
}

impl FromStr for MetricType {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "c" => Ok(MetricType::Counter),
            "g" => Ok(MetricType::Gauge),
            "h" => Ok(MetricType::Histogram),
            "m" => Ok(MetricType::Meter),
            "ms" => Ok(MetricType::Timer),
            _ => Err(Error::Parse),
        }
    }
}

/// A Metric defines a published client event.
#[derive(Debug, PartialEq)]
pub struct Metric {
    pub name: String,
    pub value: f64,
    pub metric_type: MetricType,
    pub sample_rate: Option<f64>,
}

impl Metric {
    pub fn new() -> Metric {
        Metric {
            name: String::new(),
            value: 0.0,
            metric_type: MetricType::Meter,
            sample_rate: None,
        }
    }
}

/// The parse_metric function trys to break down a single UDP packet into a single metric.
pub fn parse_metric(packet: &[u8]) -> CapellaResult<Metric> {
    lazy_static! {
        static ref PATTERN: Regex = Regex::new(r"(?x)
            \A(?P<name>[\w\.]+):
            ((?P<sign>\-|\+))?
            (?P<val>([0-9]*[.])?[0-9]+)
            \|(?P<type>\w+)
            (\|@(?P<rate>\d+\.\d+))?\z").unwrap();
    }

    if let Ok(val) = str::from_utf8(packet) {
        if !PATTERN.is_match(val) {
            return Err(Error::Parse);
        }
    } else {
        return Err(Error::Parse);
    }

    // We know this matched before so it is safe to unwrap.
    let caps = PATTERN.captures(str::from_utf8(packet).unwrap()).unwrap();

    let mut metric = Metric::new();
    // These are required to match.
    let name = caps.name("name").unwrap().as_str();
    let value = caps.name("val").unwrap().as_str().parse::<f64>().map_err(Error::from)?;
    let metric_type = caps.name("type").unwrap().as_str();

    metric.name = String::from(name);
    metric.value = value;
    metric.metric_type = metric_type.parse::<MetricType>()?;

    // Now see if there were optional values added in.
    if let Some(sign) = caps.name("sign") {
        let s = sign.as_str();
        if s == "-" {
            metric.value *= -1.0;
        }
    }

    if let Some(rate) = caps.name("rate") {
        let r = rate.as_str().parse::<f64>().map_err(Error::from)?;
        metric.sample_rate = Some(r);
    }

    Ok(metric)
}

#[cfg(test)]
mod tests {
    use super::{Metric, MetricType, parse_metric};

    #[test]
    fn bad_parse_cases() {
        let cases = vec![
            "test::1|c",
            "",
            "test|1:",
            "test:1|c@1",
            ":1.0|c",
            "test|1",
            "test:1|a",
            "test:c|c",
            "test:1|ms|0.3"
        ];
        for c in &cases {
            assert!(parse_metric(c.as_bytes()).is_err());
        }
    }

    #[test]
    fn good_simple_meter() {
        let packet = b"test:1|m";
        let m1 = parse_metric(packet).unwrap();

        let mut m2 = Metric::new();
        m2.name = String::from("test");
        m2.value = 1.0;

        assert_eq!(m1, m2);
    }

    #[test]
    fn good_timing_with_rate() {
        let packet = b"test:1|ms|@0.1";
        let m1 = parse_metric(packet).unwrap();

        let mut m2 = Metric::new();
        m2.name = String::from("test");
        m2.value = 1.0;
        m2.metric_type = MetricType::Timer;
        m2.sample_rate = Some(0.1);

        assert_eq!(m1, m2);
    }

    #[test]
    fn good_parse_float_value() {
        let packet = b"test:1.0|g";
        assert!(parse_metric(packet).is_ok());
    }

    #[test]
    fn good_nested_metric_name() {
        let packet = b"test.nested.name:1|c";
        let m1 = parse_metric(packet).unwrap();

        let mut m2 = Metric::new();
        m2.name = String::from("test.nested.name");
        m2.value = 1.0;
        m2.metric_type = MetricType::Counter;

        assert_eq!(m1, m2);
    }
}
<|endoftext|>


<commit_before>use queryst::parse;
use std::collections::HashMap;

#[derive(Debug, PartialEq)]
pub struct PageQuery {
    pub size: i64,
    pub number: i64,
}

/// JSON-API Query parameters
#[derive(Debug, PartialEq)]
pub struct Query {
    pub _type: String,
    pub include: Option<Vec<String>>,
    pub fields: Option<HashMap<String, Vec<String>>>,
    pub page: Option<PageQuery>,
}

/// JSON-API Query parameters
impl Query {
    ///
    /// Takes a query parameter string and returns a Query
    ///
    /// ```
    /// use jsonapi::query::Query;
    /// let query = Query::from_params("include=author&fields[articles]=title,\
    ///                                 body&fields[people]=name&page[number]=3&page[size]=1");
    /// match query.include {
    ///     None => assert!(false),
    ///     Some(include) => {
    ///         assert_eq!(include.len(), 1);
    ///         assert_eq!(include[0], "author");
    ///     }
    /// }
    ///
    /// ```
    pub fn from_params(params: &str) -> Self {

        match parse(params) {
            Ok(o) => {
                let include = match o.find("include") {
                    None => None,
                    Some(inc) => {
                        match inc.as_str() {
                            None => None,
                            Some(include_str) => {
                                let arr: Vec<String> =
                                    include_str.split(",").map(|s| s.to_string()).collect();
                                Some(arr)
                            }
                        }
                    }
                };

                let mut fields = HashMap::<String, Vec<String>>::new();

                o.find("fields").map(|x| if x.is_object() {
                    x.as_object().map(|obj| for (key, value) in obj.iter() {
                        let arr: Vec<String> = match value.as_str() {
                            Some(string) => {
                                let arr: Vec<String> =
                                    string.split(",").map(|s| s.to_string()).collect();
                                arr
                            }
                            None => Vec::<String>::new(),
                        };
                        fields.insert(key.to_string(), arr);

                    });
                } else {
                    println!("No fields found in {:?}", x);
                });

                let page = PageQuery {
                    number: match o.find_path(&["page", "number"]) {
                        None => 0,
                        Some(num) => {
                            if num.is_string() {
                                match num.as_str().map(str::parse::<i64>) {
                                    Some(y) => y.unwrap_or(0),
                                    None => 0,
                                }
                            } else {
                                0
                            }
                        }
                    },
                    size: match o.find_path(&["page", "size"]) {
                        None => 0,
                        Some(num) => {
                            if num.is_string() {
                                match num.as_str().map(str::parse::<i64>) {
                                    Some(y) => y.unwrap_or(0),
                                    None => 0,
                                }
                            } else {
                                0
                            }
                        }
                    },
                };

                Query {
                    _type: format!("none"),
                    include: include,
                    fields: Some(fields),
                    page: Some(page),
                }
            }
            Err(err) => {
                println!("Query: Can't parse : {:?}", err);
                Query {
                    _type: format!("none"),
                    include: None,
                    fields: None,
                    page: None,
                }
            }
        }
    }

    ///
    /// Builds a query parameter string from a Query
    ///
    /// ```
    /// use jsonapi::query::Query;
    /// let query = Query {
    ///   _type: format!("none"),
    ///   include: Some(vec!["author".into()]),
    ///   fields: None,
    ///   page: None,
    /// };
    /// match query.include {
    ///     None => assert!(false),
    ///     Some(include) => {
    ///         assert_eq!(include.len(), 1);
    ///         assert_eq!(include[0], "author");
    ///     }
    /// }
    ///
    /// ```
    pub fn to_params(&self) -> String {
        let mut params = Vec::<String>::new();

        match self.include {
            Some(ref include) => params.push(format!("include={}", include.join(","))),
            None => (),
        }

        // Examples from json-api.org,
        // fields[articles]=title,body,author&fields[people]=name
        // fields[articles]=title,body&fields[people]=name

        match self.fields {
            Some(ref fields) => {
                for (name, ref val) in fields.iter() {
                    params.push(format!("fields[{}]={}", name, val.join(",")));
                }
            }
            None => (),
        }

        match self.page {
            Some(ref page) => {
                params.push(page.to_params());
            }
            None => (),
        }

        params.join("&")
    }
}

impl PageQuery {
    pub fn to_params(&self) -> String {
        format!("page[size]={}&page[number]={}", self.size, self.number)
    }
}
<commit_msg>docs: Actually use to_params() in doc-test<commit_after>use queryst::parse;
use std::collections::HashMap;

#[derive(Debug, PartialEq)]
pub struct PageQuery {
    pub size: i64,
    pub number: i64,
}

/// JSON-API Query parameters
#[derive(Debug, PartialEq)]
pub struct Query {
    pub _type: String,
    pub include: Option<Vec<String>>,
    pub fields: Option<HashMap<String, Vec<String>>>,
    pub page: Option<PageQuery>,
}

/// JSON-API Query parameters
impl Query {
    ///
    /// Takes a query parameter string and returns a Query
    ///
    /// ```
    /// use jsonapi::query::Query;
    /// let query = Query::from_params("include=author&fields[articles]=title,\
    ///                                 body&fields[people]=name&page[number]=3&page[size]=1");
    /// match query.include {
    ///     None => assert!(false),
    ///     Some(include) => {
    ///         assert_eq!(include.len(), 1);
    ///         assert_eq!(include[0], "author");
    ///     }
    /// }
    ///
    /// ```
    pub fn from_params(params: &str) -> Self {

        match parse(params) {
            Ok(o) => {
                let include = match o.find("include") {
                    None => None,
                    Some(inc) => {
                        match inc.as_str() {
                            None => None,
                            Some(include_str) => {
                                let arr: Vec<String> =
                                    include_str.split(",").map(|s| s.to_string()).collect();
                                Some(arr)
                            }
                        }
                    }
                };

                let mut fields = HashMap::<String, Vec<String>>::new();

                o.find("fields").map(|x| if x.is_object() {
                    x.as_object().map(|obj| for (key, value) in obj.iter() {
                        let arr: Vec<String> = match value.as_str() {
                            Some(string) => {
                                let arr: Vec<String> =
                                    string.split(",").map(|s| s.to_string()).collect();
                                arr
                            }
                            None => Vec::<String>::new(),
                        };
                        fields.insert(key.to_string(), arr);

                    });
                } else {
                    println!("No fields found in {:?}", x);
                });

                let page = PageQuery {
                    number: match o.find_path(&["page", "number"]) {
                        None => 0,
                        Some(num) => {
                            if num.is_string() {
                                match num.as_str().map(str::parse::<i64>) {
                                    Some(y) => y.unwrap_or(0),
                                    None => 0,
                                }
                            } else {
                                0
                            }
                        }
                    },
                    size: match o.find_path(&["page", "size"]) {
                        None => 0,
                        Some(num) => {
                            if num.is_string() {
                                match num.as_str().map(str::parse::<i64>) {
                                    Some(y) => y.unwrap_or(0),
                                    None => 0,
                                }
                            } else {
                                0
                            }
                        }
                    },
                };

                Query {
                    _type: format!("none"),
                    include: include,
                    fields: Some(fields),
                    page: Some(page),
                }
            }
            Err(err) => {
                println!("Query: Can't parse : {:?}", err);
                Query {
                    _type: format!("none"),
                    include: None,
                    fields: None,
                    page: None,
                }
            }
        }
    }

    ///
    /// Builds a query parameter string from a Query
    ///
    /// ```
    /// use jsonapi::query::{Query, PageQuery};
    /// let query = Query {
    ///   _type: format!("none"),
    ///   include: Some(vec!["author".into()]),
    ///   fields: None,
    ///   page: Some(PageQuery {
    ///     size: 5,
    ///     number: 10,
    ///   }),
    /// };
    ///
    /// let query_string = query.to_params();
    /// assert_eq!(query_string, "include=author&page[size]=5&page[number]=10");
    ///
    /// ```
    pub fn to_params(&self) -> String {
        let mut params = Vec::<String>::new();

        match self.include {
            Some(ref include) => params.push(format!("include={}", include.join(","))),
            None => (),
        }

        // Examples from json-api.org,
        // fields[articles]=title,body,author&fields[people]=name
        // fields[articles]=title,body&fields[people]=name

        match self.fields {
            Some(ref fields) => {
                for (name, ref val) in fields.iter() {
                    params.push(format!("fields[{}]={}", name, val.join(",")));
                }
            }
            None => (),
        }

        match self.page {
            Some(ref page) => {
                params.push(page.to_params());
            }
            None => (),
        }

        params.join("&")
    }
}

impl PageQuery {
    pub fn to_params(&self) -> String {
        format!("page[size]={}&page[number]={}", self.size, self.number)
    }
}
<|endoftext|>


<commit_before>use errors::*;
use objective::*;
use fnv::FnvHashSet;
use rand::{Rng, thread_rng, sample};
use rand::distributions::{IndependentSample, Range};
use slog::{Drain, Logger};
use slog_stdlog;
use rayon::prelude::*;
use std::f64;
use std::iter::empty;

#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub enum SampleElement<Element> {
    Independent(Element),
    Dependent(Element),
}

/// Compute the total primal curvature between two sets `S` and `T`. The input `ST` should be `S ∪ T`.
///
/// # Errors
/// If $f_{el}(S) = 0$ but $f_{el}(S \cup T) \neq 0$ then `Err(DivideByZero)` is returned.
#[allow(non_snake_case)]
pub fn gamma<O: Objective>(obj: &O,
                           el: O::Element,
                           ST: &FnvHashSet<O::Element>,
                           S: &FnvHashSet<O::Element>,
                           state: &O::State,
                           joint_state: &O::State)
                           -> Result<f64> {
    let lower = obj.delta(el, S, state)?;
    let upper = obj.delta(el, ST, joint_state)?;

    if lower == upper {
        Ok(1.0)
    } else if lower == 0.0 {
        Err(ErrorKind::DivideByZero(upper, lower).into())
    } else {
        Ok(upper / lower)
    }
}

/// Compute `Λ(T, S)`. The ordering of elements `σ` is given by the ordering of `T`.
///
/// # Errors
/// If `T` contains duplicate elements, `Err(DuplicateElements)` is returned.
/// If any `Γ` returns `Err(DivideByZero)`, that is propagated.
#[allow(non_snake_case)]
pub fn lambda<O: Objective>(obj: &O,
                            T: &Vec<SampleElement<O::Element>>,
                            S: &FnvHashSet<O::Element>,
                            state: &O::State)
                            -> Result<f64> {
    let mut Ti = S.clone();
    let mut tstate = state.clone();
    let mut sum = 0.0;
    for t in T {
        if let &SampleElement::Dependent(t) = t {
            if S.contains(&t) {
                continue;
            }
            obj.insert_mut(t, &mut tstate)?;
            sum += gamma(obj, t, &Ti, S, state, &tstate)?;
            Ti.insert(t);
        } else {
            sum += 1.0; // gamma is 1 for independent elements by definition
        }
    }
    Ok(sum)
}

/// Construct a sample `T ~ D_{p,k}` where `p = bias` and `k` is given.
///
/// For worst case, bias should be 1.
#[allow(non_snake_case)]
pub fn biased_dependency_sample<O: Objective>(obj: &O,
                                              bias: f64,
                                              k: usize)
                                              -> Result<Vec<SampleElement<O::Element>>> {
    use ratio::SampleElement::*;
    let mut rng = thread_rng();
    let mut T = Vec::with_capacity(k);
    let uniform = Range::new(0.0, 1.0);

    fn choose<'a, T: 'a + Copy, I: IntoIterator<Item = &'a T>, R: Rng>(rng: &mut R,
                                                                       iter: I)
                                                                       -> Option<T> {
        let mut iter = iter.into_iter();
        if iter.size_hint().1.unwrap() == 0 {
            return None;
        }
        let range = Range::new(0, iter.size_hint().1.unwrap());
        iter.nth(range.ind_sample(rng)).cloned()
    }

    T.push(Dependent(sample(&mut rng, obj.elements(), 1)[0]));

    let deps = |t, supermodular| if let Dependent(t) = t {
        if supermodular {
            obj.supermodular_depends(t, &O::State::default())
        } else {
            obj.depends(t, &O::State::default())
        }
    } else {
        Ok(Box::new(empty::<O::Element>()) as ElementIterator<O>)
    };

    let element_count = obj.elements().count();
    let mut independent = obj.elements().collect::<FnvHashSet<_>>();
    for dep in deps(T[0], false)? {
        independent.remove(&dep);
    }
    let mut superdepends = deps(T[0], true)?.collect::<FnvHashSet<_>>();

    for i in 1..k {
        let x = if uniform.ind_sample(&mut rng) <= bias {
            // dependent
            match choose(&mut rng, &superdepends) {
                None => {
                    if superdepends.len() + T.len() == element_count {
                        // no more independent elements either
                        return Err(ErrorKind::InsufficientElements(k, i - 1).into());
                    } else {
                        Independent(choose(&mut rng, &independent).unwrap())
                    }
                }
                Some(x) => Dependent(x),
            }
        } else {
            // independent
            if independent.len() == 0 {
                // no more independent elements
                Dependent(choose(&mut rng, &superdepends).ok_or(Error::from(ErrorKind::InsufficientElements(k, i - 1)))?)
            } else {
                Independent(choose(&mut rng, &independent).unwrap())
            }
        };

        for dep in deps(x, false)? {
            independent.remove(&dep);
        }
        superdepends.extend(deps(x, true)?);

        if let Dependent(_) = x {
            assert!(!T.contains(&x));
        }
        T.push(x);

        for &t in &T {
            let t = match t {
                Dependent(t) => t,
                Independent(t) => t,
            };
            superdepends.remove(&t);
            independent.remove(&t);
        }
    }

    assert!(T.len() == k);
    Ok(T)
}

/// Find the value `x` such that `Pr[X ≤ x] ≥ δ` for random variable `X` with `n` i.i.d samples.
///
/// Uses a binary search on the `X_i` to locate a suitable value. The `gap` parameter controls how
/// precise the value found must be: once `max - min <= gap` the binary search will terminate.
///
/// TODO: this could be *much* more efficient than recomputing the cdf for every x tested.
fn empirical_cdf(samples: &Vec<f64>, delta: f64, gap: f64, log: Option<Logger>) -> Result<f64> {
    let log = log.unwrap_or_else(|| Logger::root(slog_stdlog::StdLog.fuse(), o!()));
    const ITERS_MAX: usize = 100;
    if delta > 1.0 - gap as f64 {
        warn!(log, "δ set to a higher precision than η, binary search may never converge"; "δ" => delta, "η" => gap);
    }
    let n = samples.len();
    let mut max =
        samples.iter().filter(|&f| !f.is_nan()).fold(f64::NEG_INFINITY,
                                                     |max, &cur| if cur > max { cur } else { max });
    let mut min = samples.iter()
        .filter(|&f| !f.is_nan())
        .fold(f64::INFINITY, |min, &cur| if cur < min { cur } else { min });
    assert!(max.is_finite());
    assert!(min.is_finite());

    let cdf = |x| samples.iter().filter(|&xi| *xi <= x).count() as f64 / n as f64;
    let mut iters = 0;

    loop {
        let x = (max + min) / 2.0;
        assert!(x.is_finite());
        debug!(log, "testing"; "x" => x, "gap" => max - min, "max" => max, "min" => min);

        let p = cdf(x);

        if p >= delta && max - min <= gap {
            info!(log, "found x with gap satisfied"; "x" => x, "gap" => max - min, "p" => p);
            return Ok(x);
        } else if max - min <= gap {
            // gotten small enough
            let p = cdf(max);
            assert!(p >= delta);
            info!(log, "gap satisfied, but x not found. using max"; "x" => max, "gap" => max - min, "p" => p);
            return Ok(max);
        }

        if p >= delta {
            debug!(log, "p >= δ"; "p" => p);
            // decrease max
            max = x;
        } else {
            debug!(log, "p < δ"; "p" => p);
            // increase min
            min = x;
        }
        iters += 1;
        if iters > ITERS_MAX {
            crit!(log, "binary search failed to converge (likely due to loss of precision from an abnormal number of ζ events)"; "min" => min, "max" => max, "gap" => max - min, "η" => gap);
            return Err(ErrorKind::NoConvergence(min, max, iters).into());
        }
    }
}

pub fn sample_lambda<O: Objective + Sync>(obj: &O,
                                          sol: &FnvHashSet<O::Element>,
                                          state: O::State,
                                          bias: f64,
                                          k: usize,
                                          num_samples: usize)
                                          -> Result<Vec<f64>>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let mut sample_vec = Vec::with_capacity(num_samples);

    (0..num_samples)
        .into_par_iter()
        .map(|_| {
            let sample = biased_dependency_sample(obj, bias, k)?;
            lambda(obj, &sample, sol, &state)
        })
        .collect_into(&mut sample_vec);

    sample_vec.into_iter().collect::<Result<Vec<_>>>()
}

pub fn estimate_lambda<O: Objective + Sync>(obj: &O,
                                            sol: &FnvHashSet<O::Element>,
                                            state: O::State,
                                            bias: f64,
                                            k: usize,
                                            eps: f64,
                                            delta: f64,
                                            delta2: f64,
                                            eta: f64,
                                            log: Option<Logger>)
                                            -> Result<f64>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let log = log.unwrap_or_else(|| Logger::root(slog_stdlog::StdLog.fuse(), o!()));

    let num_samples = (1.0 - delta2).ln() / (-2.0 * eps.powi(2));
    // assert!(eps >= (1.0 / (2.0 * num_samples) * 2f64.ln()).sqrt()); // sufficiency condition for the bound.
    assert!((-2.0 * eps.powi(2) * num_samples).exp() <= 0.5);

    let num_samples = num_samples.ceil() as usize;
    info!(log, "constructing samples"; "num_samples" => num_samples);

    let samples = sample_lambda(obj, sol, state, bias, k, num_samples)?;
    assert!(samples.len() == num_samples);
    let num_nans: usize = samples.iter().filter(|&f| f.is_nan() || f.is_infinite()).count();

    if num_nans > 0 {
        crit!(log, "samples contain NaN / infinite values"; "count" => num_nans);
        panic!("samples contain NaN / infinite values");
    }

    let mean = samples.iter().sum::<f64>() / samples.len() as f64;
    let variance = samples.iter().map(|xi| (xi - mean).powi(2)).sum::<f64>() / samples.len() as f64;
    let min = samples.iter().fold(f64::INFINITY, |min, xi| xi.min(min));
    let max = samples.iter().fold(0.0, |max, xi| xi.max(max));

    info!(log, "done sampling"; "mean" => mean, "variance" => variance, "min" => min, "max" => max);

    info!(log, "estimating cdf"; "η" => eta);
    empirical_cdf(&samples, delta, eta, Some(log.new(o!("section" => "cdf"))))
}
<commit_msg>Added chebyshev estimation (w/ fixed # of samples)<commit_after>use errors::*;
use objective::*;
use fnv::FnvHashSet;
use rand::{Rng, thread_rng, sample};
use rand::distributions::{IndependentSample, Range};
use slog::{Drain, Logger};
use slog_stdlog;
use rayon::prelude::*;
use std::f64;
use std::iter::empty;

#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub enum SampleElement<Element> {
    Independent(Element),
    Dependent(Element),
}

/// Compute the total primal curvature between two sets `S` and `T`. The input `ST` should be `S ∪ T`.
///
/// # Errors
/// If $f_{el}(S) = 0$ but $f_{el}(S \cup T) \neq 0$ then `Err(DivideByZero)` is returned.
#[allow(non_snake_case)]
pub fn gamma<O: Objective>(obj: &O,
                           el: O::Element,
                           ST: &FnvHashSet<O::Element>,
                           S: &FnvHashSet<O::Element>,
                           state: &O::State,
                           joint_state: &O::State)
                           -> Result<f64> {
    let lower = obj.delta(el, S, state)?;
    let upper = obj.delta(el, ST, joint_state)?;

    if lower == upper {
        Ok(1.0)
    } else if lower == 0.0 {
        Err(ErrorKind::DivideByZero(upper, lower).into())
    } else {
        Ok(upper / lower)
    }
}

/// Compute `Λ(T, S)`. The ordering of elements `σ` is given by the ordering of `T`.
///
/// # Errors
/// If `T` contains duplicate elements, `Err(DuplicateElements)` is returned.
/// If any `Γ` returns `Err(DivideByZero)`, that is propagated.
#[allow(non_snake_case)]
pub fn lambda<O: Objective>(obj: &O,
                            T: &Vec<SampleElement<O::Element>>,
                            S: &FnvHashSet<O::Element>,
                            state: &O::State)
                            -> Result<f64> {
    let mut Ti = S.clone();
    let mut tstate = state.clone();
    let mut sum = 0.0;
    for t in T {
        if let &SampleElement::Dependent(t) = t {
            if S.contains(&t) {
                continue;
            }
            obj.insert_mut(t, &mut tstate)?;
            sum += gamma(obj, t, &Ti, S, state, &tstate)?;
            Ti.insert(t);
        } else {
            sum += 1.0; // gamma is 1 for independent elements by definition
        }
    }
    Ok(sum)
}

/// Construct a sample `T ~ D_{p,k}` where `p = bias` and `k` is given.
///
/// For worst case, bias should be 1.
#[allow(non_snake_case)]
pub fn biased_dependency_sample<O: Objective>(obj: &O,
                                              bias: f64,
                                              k: usize)
                                              -> Result<Vec<SampleElement<O::Element>>> {
    use ratio::SampleElement::*;
    let mut rng = thread_rng();
    let mut T = Vec::with_capacity(k);
    let uniform = Range::new(0.0, 1.0);

    fn choose<'a, T: 'a + Copy, I: IntoIterator<Item = &'a T>, R: Rng>(rng: &mut R,
                                                                       iter: I)
                                                                       -> Option<T> {
        let mut iter = iter.into_iter();
        if iter.size_hint().1.unwrap() == 0 {
            return None;
        }
        let range = Range::new(0, iter.size_hint().1.unwrap());
        iter.nth(range.ind_sample(rng)).cloned()
    }

    T.push(Dependent(sample(&mut rng, obj.elements(), 1)[0]));

    let deps = |t, supermodular| if let Dependent(t) = t {
        if supermodular {
            obj.supermodular_depends(t, &O::State::default())
        } else {
            obj.depends(t, &O::State::default())
        }
    } else {
        Ok(Box::new(empty::<O::Element>()) as ElementIterator<O>)
    };

    let element_count = obj.elements().count();
    let mut independent = obj.elements().collect::<FnvHashSet<_>>();
    for dep in deps(T[0], false)? {
        independent.remove(&dep);
    }
    let mut superdepends = deps(T[0], true)?.collect::<FnvHashSet<_>>();

    for i in 1..k {
        let x = if uniform.ind_sample(&mut rng) <= bias {
            // dependent
            match choose(&mut rng, &superdepends) {
                None => {
                    if superdepends.len() + T.len() == element_count {
                        // no more independent elements either
                        return Err(ErrorKind::InsufficientElements(k, i - 1).into());
                    } else {
                        Independent(choose(&mut rng, &independent).unwrap())
                    }
                }
                Some(x) => Dependent(x),
            }
        } else {
            // independent
            if independent.len() == 0 {
                // no more independent elements
                Dependent(choose(&mut rng, &superdepends).ok_or(Error::from(ErrorKind::InsufficientElements(k, i - 1)))?)
            } else {
                Independent(choose(&mut rng, &independent).unwrap())
            }
        };

        for dep in deps(x, false)? {
            independent.remove(&dep);
        }
        superdepends.extend(deps(x, true)?);

        if let Dependent(_) = x {
            assert!(!T.contains(&x));
        }
        T.push(x);

        for &t in &T {
            let t = match t {
                Dependent(t) => t,
                Independent(t) => t,
            };
            superdepends.remove(&t);
            independent.remove(&t);
        }
    }

    assert!(T.len() == k);
    Ok(T)
}

/// Find the value `x` such that `Pr[X ≤ x] ≥ δ` for random variable `X` with `n` i.i.d samples.
///
/// Uses a binary search on the `X_i` to locate a suitable value. The `gap` parameter controls how
/// precise the value found must be: once `max - min <= gap` the binary search will terminate.
///
/// TODO: this could be *much* more efficient than recomputing the cdf for every x tested.
fn empirical_cdf(samples: &Vec<f64>, delta: f64, gap: f64, log: Option<Logger>) -> Result<f64> {
    let log = log.unwrap_or_else(|| Logger::root(slog_stdlog::StdLog.fuse(), o!()));
    const ITERS_MAX: usize = 100;
    if delta > 1.0 - gap as f64 {
        warn!(log, "δ set to a higher precision than η, binary search may never converge"; "δ" => delta, "η" => gap);
    }
    let n = samples.len();
    let mut max =
        samples.iter().filter(|&f| !f.is_nan()).fold(f64::NEG_INFINITY,
                                                     |max, &cur| if cur > max { cur } else { max });
    let mut min = samples.iter()
        .filter(|&f| !f.is_nan())
        .fold(f64::INFINITY, |min, &cur| if cur < min { cur } else { min });
    assert!(max.is_finite());
    assert!(min.is_finite());

    let cdf = |x| samples.iter().filter(|&xi| *xi <= x).count() as f64 / n as f64;
    let mut iters = 0;

    loop {
        let x = (max + min) / 2.0;
        assert!(x.is_finite());
        debug!(log, "testing"; "x" => x, "gap" => max - min, "max" => max, "min" => min);

        let p = cdf(x);

        if p >= delta && max - min <= gap {
            info!(log, "found x with gap satisfied"; "x" => x, "gap" => max - min, "p" => p);
            return Ok(x);
        } else if max - min <= gap {
            // gotten small enough
            let p = cdf(max);
            assert!(p >= delta);
            info!(log, "gap satisfied, but x not found. using max"; "x" => max, "gap" => max - min, "p" => p);
            return Ok(max);
        }

        if p >= delta {
            debug!(log, "p >= δ"; "p" => p);
            // decrease max
            max = x;
        } else {
            debug!(log, "p < δ"; "p" => p);
            // increase min
            min = x;
        }
        iters += 1;
        if iters > ITERS_MAX {
            crit!(log, "binary search failed to converge (likely due to loss of precision from an abnormal number of ζ events)"; "min" => min, "max" => max, "gap" => max - min, "η" => gap);
            return Err(ErrorKind::NoConvergence(min, max, iters).into());
        }
    }
}

pub fn sample_lambda<O: Objective + Sync>(obj: &O,
                                          sol: &FnvHashSet<O::Element>,
                                          state: O::State,
                                          bias: f64,
                                          k: usize,
                                          num_samples: usize)
                                          -> Result<Vec<f64>>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let mut sample_vec = Vec::with_capacity(num_samples);

    (0..num_samples)
        .into_par_iter()
        .map(|_| {
            let sample = biased_dependency_sample(obj, bias, k)?;
            lambda(obj, &sample, sol, &state)
        })
        .collect_into(&mut sample_vec);

    sample_vec.into_iter().collect::<Result<Vec<_>>>()
}

pub fn estimate_lambda<O: Objective + Sync>(obj: &O,
                                            sol: &FnvHashSet<O::Element>,
                                            state: O::State,
                                            bias: f64,
                                            k: usize,
                                            eps: f64,
                                            delta: f64,
                                            delta2: f64,
                                            eta: f64,
                                            log: Option<Logger>)
                                            -> Result<f64>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let log = log.unwrap_or_else(|| Logger::root(slog_stdlog::StdLog.fuse(), o!()));

    let num_samples = (1.0 - delta2).ln() / (-2.0 * eps.powi(2));
    // assert!(eps >= (1.0 / (2.0 * num_samples) * 2f64.ln()).sqrt()); // sufficiency condition for the bound.
    assert!((-2.0 * eps.powi(2) * num_samples).exp() <= 0.5);

    let num_samples = num_samples.ceil() as usize;
    info!(log, "constructing samples"; "num_samples" => num_samples);

    let samples = sample_lambda(obj, sol, state, bias, k, num_samples)?;
    assert!(samples.len() == num_samples);
    let num_nans: usize = samples.iter().filter(|&f| f.is_nan() || f.is_infinite()).count();

    if num_nans > 0 {
        crit!(log, "samples contain NaN / infinite values"; "count" => num_nans);
        panic!("samples contain NaN / infinite values");
    }

    let mean = samples.iter().sum::<f64>() / samples.len() as f64;
    let variance = samples.iter().map(|xi| (xi - mean).powi(2)).sum::<f64>() / samples.len() as f64;
    let min = samples.iter().fold(f64::INFINITY, |min, xi| xi.min(min));
    let max = samples.iter().fold(0.0, |max, xi| xi.max(max));

    info!(log, "done sampling"; "mean" => mean, "variance" => variance, "min" => min, "max" => max);

    info!(log, "estimating cdf"; "η" => eta);
    empirical_cdf(&samples, delta, eta, Some(log.new(o!("section" => "cdf"))))
}

pub fn estimate_lambda_chebyshev<O: Objective + Sync>(obj: &O,
                                                      sol: &FnvHashSet<O::Element>,
                                                      state: O::State,
                                                      bias: f64,
                                                      k: usize,
                                                      delta: f64,
                                                      num_samples: usize,
                                                      log: Option<Logger>)
                                                      -> Result<f64>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let log = log.unwrap_or_else(|| Logger::root(slog_stdlog::StdLog.fuse(), o!()));

    info!(log, "constructing samples"; "num_samples" => num_samples);

    let samples = sample_lambda(obj, sol, state, bias, k, num_samples)?;
    assert!(samples.len() == num_samples);
    let num_nans: usize = samples.iter().filter(|&f| f.is_nan() || f.is_infinite()).count();

    if num_nans > 0 {
        crit!(log, "samples contain NaN / infinite values"; "count" => num_nans);
        panic!("samples contain NaN / infinite values");
    }

    let mean = samples.iter().sum::<f64>() / samples.len() as f64;
    let variance = samples.iter().map(|xi| (xi - mean).powi(2)).sum::<f64>() / samples.len() as f64;
    let stddev = variance.sqrt();
    let min = samples.iter().fold(f64::INFINITY, |min, xi| xi.min(min));
    let max = samples.iter().fold(0.0, |max, xi| xi.max(max));

    info!(log, "done sampling"; "mean" => mean, "variance" => variance, "std. dev." => stddev, "min" => min, "max" => max);
    let factor = (1.0 / (1.0 - delta)).sqrt(); // d = 1 - 1/k^2 -> 1/k^2 = 1 - d -> k^2 = 1/(1 - d) -> sqrt(1/(1 - d))
    Ok(mean + factor * stddev)
}
<|endoftext|>