<commit_before>use crate::route_guide::Feature;
use crate::route_guide::Point;
use crate::route_guide::Rectangle;
use crate::route_guide::RouteNote;
use crate::route_guide::RouteSummary;
use crate::route_guide_grpc::RouteGuide;

use futures::future;

use futures::stream;
use futures::TryFutureExt;
use futures::TryStreamExt;

use futures::stream::StreamExt;

use grpc::Metadata;
use grpc::ServerHandlerContext;
use grpc::ServerRequest;
use grpc::ServerRequestSingle;
use grpc::ServerResponseSink;
use grpc::ServerResponseUnarySink;
use json::JsonValue;
use std::collections::HashMap;
use std::f64;
use std::fs;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use std::task::Poll;
use std::time::Instant;

// https://github.com/grpc/grpc-go/blob/master/examples/route_guide/server/server.go
#[derive(Default)]
pub struct RouteGuideImpl {
    saved_features: Vec<Feature>,
    route_notes: Arc<Mutex<HashMap<String, Vec<RouteNote>>>>,
}

impl RouteGuideImpl {
    pub fn new_and_load_db() -> RouteGuideImpl {
        RouteGuideImpl {
            saved_features: load_features(Path::new(ROUTE_GUIDE_DB_PATH)),
            route_notes: Default::default(),
        }
    }
}

impl RouteGuide for RouteGuideImpl {
    fn get_feature(
        &self,
        _o: ServerHandlerContext,
        req: ServerRequestSingle<Point>,
        resp: ServerResponseUnarySink<Feature>,
    ) -> grpc::Result<()> {
        for feature in &*self.saved_features {
            if feature.get_location() == &req.message {
                return resp.finish(feature.clone());
            }
        }

        resp.finish(Feature {
            location: Some(req.message).into(),
            ..Default::default()
        })
    }

    fn list_features(
        &self,
        o: ServerHandlerContext,
        mut req: ServerRequestSingle<Rectangle>,
        resp: ServerResponseSink<Feature>,
    ) -> grpc::Result<()> {
        let req = req.take_message();
        // TODO: do not clone
        let stream = stream::iter(self.saved_features.clone())
            .filter_map(move |feature| {
                future::ready({
                    if in_range(feature.get_location(), &req) {
                        Some(feature)
                    } else {
                        None
                    }
                })
            })
            .map(Ok);
        o.pump(stream, resp);
        Ok(())
    }

    fn record_route(
        &self,
        o: ServerHandlerContext,
        req: ServerRequest<Point>,
        resp: ServerResponseUnarySink<RouteSummary>,
    ) -> grpc::Result<()> {
        let start_time = Instant::now();

        struct State {
            point_count: u32,
            feature_count: u32,
            distance: u32,
            last_point: Option<Point>,
        }

        let state = State {
            point_count: 0,
            feature_count: 0,
            distance: 0,
            last_point: None,
        };

        let saved_features = self.saved_features.clone();

        let f = req
            .into_stream()
            .try_fold(state, move |mut state, point| {
                state.point_count += 1;
                for feature in &saved_features {
                    if feature.get_location() == &point {
                        state.feature_count += 1;
                    }
                }
                if let Some(last_point) = &state.last_point {
                    state.distance += calc_distance(last_point, &point);
                }
                state.last_point = Some(point);
                future::ok::<_, grpc::Error>(state)
            })
            .map_ok(move |state| RouteSummary {
                point_count: state.point_count as i32,
                feature_count: state.feature_count as i32,
                distance: state.distance as i32,
                elapsed_time: start_time.elapsed().as_secs() as i32,
                ..Default::default()
            });
        o.pump_future(f, resp);
        Ok(())
    }

    fn route_chat(
        &self,
        o: ServerHandlerContext,
        req: ServerRequest<RouteNote>,
        mut resp: ServerResponseSink<RouteNote>,
    ) -> grpc::Result<()> {
        let mut req = req.into_stream();

        let route_notes_map = self.route_notes.clone();

        o.spawn_poll_fn(move |cx| {
            loop {
                // Wait until resp is writable
                if let Poll::Pending = resp.poll(cx)? {
                    return Poll::Pending;
                }

                match req.poll_next_unpin(cx)? {
                    Poll::Pending => return Poll::Pending,
                    Poll::Ready(Some(note)) => {
                        let key = serialize(note.get_location());

                        let mut route_notes_map = route_notes_map.lock().unwrap();

                        let route_notes = route_notes_map.entry(key).or_insert(Vec::new());
                        route_notes.push(note);

                        for note in route_notes {
                            resp.send_data(note.clone())?;
                        }
                    }
                    Poll::Ready(None) => {
                        resp.send_trailers(Metadata::new())?;
                        return Poll::Ready(Ok(()));
                    }
                }
            }
        });
        Ok(())
    }
}

fn in_range(point: &Point, rect: &Rectangle) -> bool {
    let left = f64::min(
        rect.get_lo().longitude as f64,
        rect.get_hi().longitude as f64,
    );
    let right = f64::max(
        rect.get_lo().longitude as f64,
        rect.get_hi().longitude as f64,
    );
    let top = f64::max(rect.get_lo().latitude as f64, rect.get_hi().latitude as f64);
    let bottom = f64::min(rect.get_lo().latitude as f64, rect.get_hi().latitude as f64);

    point.longitude as f64 >= left
        && point.longitude as f64 <= right
        && point.latitude as f64 >= bottom
        && point.latitude as f64 <= top
}

fn to_radians(num: f64) -> f64 {
    num * f64::consts::PI / 180.
}

fn calc_distance(p1: &Point, p2: &Point) -> u32 {
    let cord_factor: f64 = 1e7;
    let r = 6371000.; // earth radius in metres
    let lat1 = to_radians(p1.latitude as f64 / cord_factor);
    let lat2 = to_radians(p2.latitude as f64 / cord_factor);
    let lng1 = to_radians(p1.longitude as f64 / cord_factor);
    let lng2 = to_radians(p2.longitude as f64 / cord_factor);
    let dlat = lat2 - lat1;
    let dlng = lng2 - lng1;

    let a = f64::sin(dlat / 2.) * f64::sin(dlat / 2.)
        + f64::cos(lat1) * f64::cos(lat2) * f64::sin(dlng / 2.) * f64::sin(dlng / 2.);
    let c = 2. * f64::atan2(f64::sqrt(a), f64::sqrt(1. - a));

    let distance = r * c;
    distance as u32
}

fn serialize(point: &Point) -> String {
    format!("{} {}", point.latitude, point.longitude)
}

const ROUTE_GUIDE_DB_PATH: &str = "testdata/route_guide_db.json";

fn load_features(path: &Path) -> Vec<Feature> {
    let mut file = fs::File::open(path).expect("open");
    let mut s = String::new();
    file.read_to_string(&mut s).expect("read");

    // TODO: use protobuf mapper when new version is released

    let json_value = json::parse(&s).expect("parse json");
    let array = match json_value {
        JsonValue::Array(array) => array,
        _ => panic!(),
    };

    array
        .into_iter()
        .map(|item| {
            let object = match item {
                JsonValue::Object(object) => object,
                _ => panic!(),
            };

            let location = match object.get("location").expect("location") {
                JsonValue::Object(object) => object,
                _ => panic!(),
            };

            Feature {
                name: object
                    .get("name")
                    .expect("name")
                    .as_str()
                    .expect("unwrap")
                    .to_owned(),
                location: Some(Point {
                    latitude: location
                        .get("latitude")
                        .expect("latitude")
                        .as_i32()
                        .unwrap(),
                    longitude: location
                        .get("longitude")
                        .expect("longitude")
                        .as_i32()
                        .unwrap(),
                    ..Default::default()
                })
                .into(),
                ..Default::default()
            }
        })
        .collect()
}

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

    #[test]
    fn test_load_features() {
        let features = load_features(Path::new(ROUTE_GUIDE_DB_PATH));
        assert!(features.len() > 0);
    }
}
<commit_msg>Do not clone saved_features in route guide server example<commit_after>use crate::route_guide::Feature;
use crate::route_guide::Point;
use crate::route_guide::Rectangle;
use crate::route_guide::RouteNote;
use crate::route_guide::RouteSummary;
use crate::route_guide_grpc::RouteGuide;

use futures::future;

use futures::stream;
use futures::TryFutureExt;
use futures::TryStreamExt;

use futures::stream::StreamExt;

use grpc::Metadata;
use grpc::ServerHandlerContext;
use grpc::ServerRequest;
use grpc::ServerRequestSingle;
use grpc::ServerResponseSink;
use grpc::ServerResponseUnarySink;
use json::JsonValue;
use std::collections::HashMap;
use std::f64;
use std::fs;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use std::task::Poll;
use std::time::Instant;

// https://github.com/grpc/grpc-go/blob/master/examples/route_guide/server/server.go
#[derive(Default)]
pub struct RouteGuideImpl {
    saved_features: Arc<Vec<Feature>>,
    route_notes: Arc<Mutex<HashMap<String, Vec<RouteNote>>>>,
}

impl RouteGuideImpl {
    pub fn new_and_load_db() -> RouteGuideImpl {
        RouteGuideImpl {
            saved_features: Arc::new(load_features(Path::new(ROUTE_GUIDE_DB_PATH))),
            route_notes: Default::default(),
        }
    }
}

impl RouteGuide for RouteGuideImpl {
    fn get_feature(
        &self,
        _o: ServerHandlerContext,
        req: ServerRequestSingle<Point>,
        resp: ServerResponseUnarySink<Feature>,
    ) -> grpc::Result<()> {
        for feature in &*self.saved_features {
            if feature.get_location() == &req.message {
                return resp.finish(feature.clone());
            }
        }

        resp.finish(Feature {
            location: Some(req.message).into(),
            ..Default::default()
        })
    }

    fn list_features(
        &self,
        o: ServerHandlerContext,
        mut req: ServerRequestSingle<Rectangle>,
        resp: ServerResponseSink<Feature>,
    ) -> grpc::Result<()> {
        let req = req.take_message();
        let saved_features = self.saved_features.clone();
        let stream = stream::iter(0..saved_features.len())
            .filter_map(move |i| {
                let feature = saved_features[i].clone();
                future::ready({
                    if in_range(feature.get_location(), &req) {
                        Some(feature)
                    } else {
                        None
                    }
                })
            })
            .map(Ok);
        o.pump(stream, resp);
        Ok(())
    }

    fn record_route(
        &self,
        o: ServerHandlerContext,
        req: ServerRequest<Point>,
        resp: ServerResponseUnarySink<RouteSummary>,
    ) -> grpc::Result<()> {
        let start_time = Instant::now();

        struct State {
            point_count: u32,
            feature_count: u32,
            distance: u32,
            last_point: Option<Point>,
        }

        let state = State {
            point_count: 0,
            feature_count: 0,
            distance: 0,
            last_point: None,
        };

        let saved_features = self.saved_features.clone();

        let f = req
            .into_stream()
            .try_fold(state, move |mut state, point| {
                state.point_count += 1;
                for feature in &saved_features[..] {
                    if feature.get_location() == &point {
                        state.feature_count += 1;
                    }
                }
                if let Some(last_point) = &state.last_point {
                    state.distance += calc_distance(last_point, &point);
                }
                state.last_point = Some(point);
                future::ok::<_, grpc::Error>(state)
            })
            .map_ok(move |state| RouteSummary {
                point_count: state.point_count as i32,
                feature_count: state.feature_count as i32,
                distance: state.distance as i32,
                elapsed_time: start_time.elapsed().as_secs() as i32,
                ..Default::default()
            });
        o.pump_future(f, resp);
        Ok(())
    }

    fn route_chat(
        &self,
        o: ServerHandlerContext,
        req: ServerRequest<RouteNote>,
        mut resp: ServerResponseSink<RouteNote>,
    ) -> grpc::Result<()> {
        let mut req = req.into_stream();

        let route_notes_map = self.route_notes.clone();

        o.spawn_poll_fn(move |cx| {
            loop {
                // Wait until resp is writable
                if let Poll::Pending = resp.poll(cx)? {
                    return Poll::Pending;
                }

                match req.poll_next_unpin(cx)? {
                    Poll::Pending => return Poll::Pending,
                    Poll::Ready(Some(note)) => {
                        let key = serialize(note.get_location());

                        let mut route_notes_map = route_notes_map.lock().unwrap();

                        let route_notes = route_notes_map.entry(key).or_insert(Vec::new());
                        route_notes.push(note);

                        for note in route_notes {
                            resp.send_data(note.clone())?;
                        }
                    }
                    Poll::Ready(None) => {
                        resp.send_trailers(Metadata::new())?;
                        return Poll::Ready(Ok(()));
                    }
                }
            }
        });
        Ok(())
    }
}

fn in_range(point: &Point, rect: &Rectangle) -> bool {
    let left = f64::min(
        rect.get_lo().longitude as f64,
        rect.get_hi().longitude as f64,
    );
    let right = f64::max(
        rect.get_lo().longitude as f64,
        rect.get_hi().longitude as f64,
    );
    let top = f64::max(rect.get_lo().latitude as f64, rect.get_hi().latitude as f64);
    let bottom = f64::min(rect.get_lo().latitude as f64, rect.get_hi().latitude as f64);

    point.longitude as f64 >= left
        && point.longitude as f64 <= right
        && point.latitude as f64 >= bottom
        && point.latitude as f64 <= top
}

fn to_radians(num: f64) -> f64 {
    num * f64::consts::PI / 180.
}

fn calc_distance(p1: &Point, p2: &Point) -> u32 {
    let cord_factor: f64 = 1e7;
    let r = 6371000.; // earth radius in metres
    let lat1 = to_radians(p1.latitude as f64 / cord_factor);
    let lat2 = to_radians(p2.latitude as f64 / cord_factor);
    let lng1 = to_radians(p1.longitude as f64 / cord_factor);
    let lng2 = to_radians(p2.longitude as f64 / cord_factor);
    let dlat = lat2 - lat1;
    let dlng = lng2 - lng1;

    let a = f64::sin(dlat / 2.) * f64::sin(dlat / 2.)
        + f64::cos(lat1) * f64::cos(lat2) * f64::sin(dlng / 2.) * f64::sin(dlng / 2.);
    let c = 2. * f64::atan2(f64::sqrt(a), f64::sqrt(1. - a));

    let distance = r * c;
    distance as u32
}

fn serialize(point: &Point) -> String {
    format!("{} {}", point.latitude, point.longitude)
}

const ROUTE_GUIDE_DB_PATH: &str = "testdata/route_guide_db.json";

fn load_features(path: &Path) -> Vec<Feature> {
    let mut file = fs::File::open(path).expect("open");
    let mut s = String::new();
    file.read_to_string(&mut s).expect("read");

    // TODO: use protobuf mapper when new version is released

    let json_value = json::parse(&s).expect("parse json");
    let array = match json_value {
        JsonValue::Array(array) => array,
        _ => panic!(),
    };

    array
        .into_iter()
        .map(|item| {
            let object = match item {
                JsonValue::Object(object) => object,
                _ => panic!(),
            };

            let location = match object.get("location").expect("location") {
                JsonValue::Object(object) => object,
                _ => panic!(),
            };

            Feature {
                name: object
                    .get("name")
                    .expect("name")
                    .as_str()
                    .expect("unwrap")
                    .to_owned(),
                location: Some(Point {
                    latitude: location
                        .get("latitude")
                        .expect("latitude")
                        .as_i32()
                        .unwrap(),
                    longitude: location
                        .get("longitude")
                        .expect("longitude")
                        .as_i32()
                        .unwrap(),
                    ..Default::default()
                })
                .into(),
                ..Default::default()
            }
        })
        .collect()
}

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

    #[test]
    fn test_load_features() {
        let features = load_features(Path::new(ROUTE_GUIDE_DB_PATH));
        assert!(features.len() > 0);
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Pierre Marijon.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! GFF3 format reading and writing.
//!
//! GFF2 definition : http://gmod.org/wiki/GFF2#The_GFF2_File_Format (not yet support)
//! GTF2 definition : http://mblab.wustl.edu/GTF2.html (not yet support)
//! GFF3 definition : http://gmod.org/wiki/GFF3#GFF3_Format
//!
//! # Example
//!
//! ```
//! use std::io;
//! use bio::io::gff;
//! let reader = gff::Reader::new(io::stdin());
//! ```

use std::io;
use std::fs;
use std::path::Path;
use std::convert::AsRef;
use std::collections::HashMap;

use csv;

use io::Strand;

/// A GFF reader.
pub struct Reader<R: io::Read> {
    inner: csv::Reader<R>,
}

impl Reader<fs::File> {
    /// Read GFF from given file path.
    pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        fs::File::open(path).map(Reader::new)
    }
}

impl<R: io::Read> Reader<R> {
    /// Create a new GFF reader given an instance of `io::Read`.
    pub fn new(reader: R) -> Self {
        Reader {
            inner: csv::Reader::from_reader(reader).delimiter(b'\t').has_headers(false)
        }
    }

    /// Iterate over all records.
    pub fn records(&mut self) -> Records<R> {
        Records { inner: self.inner.decode() }
    }
}

/// A GFF record.
pub struct Records<'a, R: 'a + io::Read> {
    inner: csv::DecodedRecords<'a, R, (String, String, String, u64, u64, String, String, String, String)>,
}

impl<'a, R: io::Read> Iterator for Records<'a, R> {
    type Item = csv::Result<Record>;

    fn next(&mut self) -> Option<csv::Result<Record>> {
        self.inner.next().map(|res| {
            res.map(|(seqname, source, feature_type, start, end, score, strand, frame, attributes)| {
                Record {
                    seqname: seqname,
                    source: source,
                    feature_type: feature_type,
                    start: start,
                    end: end,
                    score: score,
                    strand: strand,
                    frame: frame,
                    attributes: csv::Reader::from_string(attributes)
                        .delimiter(b'=')
                        .record_terminator(csv::RecordTerminator::Any(b';'))
                        .has_headers(false)
                        .decode().collect::<csv::Result<HashMap<String, String>>>().unwrap(),
                }
            })
        })
    }
}

/// A GFF writer.
pub struct Writer<W: io::Write> {
    inner: csv::Writer<W>,
}

impl Writer<fs::File> {
    /// Write to a given file path.
    pub fn to_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        fs::File::create(path).map(Writer::new)
    }
}

impl<W: io::Write> Writer<W> {
    /// Write to a given writer.
    pub fn new(writer: W) -> Self {
        Writer { inner: csv::Writer::from_writer(writer).delimiter(b'\t').flexible(true) }
    }

    /// Write a given GFF record.
    pub fn write(&mut self, record: Record) -> csv::Result<()> {
        let attributes;
        if !record.attributes.is_empty() {
            attributes = record.attributes.iter().map(|(a, b)| format!("{}={}", a, b)).collect::<Vec<_>>().join(";");
        } else {
            attributes = "".to_owned();
        }
        self.inner.encode((record.seqname, record.source, record.feature_type, record.start, record.end, record.score, record.strand, record.frame, attributes))
    }
}

/// A GFF record
#[derive(RustcEncodable)]
pub struct Record {
    seqname: String,
    source: String,
    feature_type: String,
    start: u64,
    end: u64,
    score: String,
    strand: String,
    frame: String,
    attributes: HashMap<String, String>,
}

impl Record {
    /// Create a new GFF record.
    pub fn new() -> Self {
        Record {
            seqname: "".to_owned(),
            source: "".to_owned(),
            feature_type: "".to_owned(),
            start: 0,
            end: 0,
            score: ".".to_owned(),
            strand: ".".to_owned(),
            frame: "".to_owned(),
            attributes: HashMap::<String, String>::new(),
        }
    }

    /// Sequence name of the feature.
    pub fn seqname(&self) -> &str {
        &self.seqname
    }

    /// Source of the feature.
    pub fn source(&self) -> &str {
        &self.source
    }

    /// Type of the feature.
    pub fn feature_type(&self) -> &str {
        &self.feature_type
    }

    /// Start position of feature (1-based).
    pub fn start(&self) -> &u64 {
        &self.start
    }

    /// End position of feature (1-based, not included).
    pub fn end(&self) -> &u64 {
        &self.end
    }

    /// Score of feature
    pub fn score(&self) -> Option<u64> {
        match self.score.as_ref() {
            "." => None,
            _ => self.score.parse::<u64>().ok(),
        }
    }

    /// Strand of the feature.
    pub fn strand(&self) -> Option<Strand> {
        match self.strand.as_ref() {
            "+" => Some(Strand::Forward),
            "-" => Some(Strand::Reverse),
            _ => None,
        }
    }

    /// Frame of the feature.
    pub fn frame(&self) -> &str {
        &self.frame
    }

    /// Attribute of feature
    pub fn attributes(&self) -> &HashMap<String, String> {
        &self.attributes
    }

    /// Get mutable reference on seqname of feature.
    pub fn seqname_mut(&mut self) -> &mut str {
        return &mut *self.seqname;
    }

    /// Get mutable reference on source of feature.
    pub fn source_mut(&mut self) -> &mut str {
        return &mut *self.source;
    }

    /// Get mutable reference on type of feature.
    pub fn feature_type_mut(&mut self) -> &mut str {
        return &mut *self.feature_type;
    }

    /// Get mutable reference on start of feature.
    pub fn start_mut(&mut self) -> &mut u64 {
        return &mut self.start;
    }

    /// Get mutable reference on end of feature.
    pub fn end_mut(&mut self) -> &mut u64 {
        return &mut self.end;
    }

    /// Get mutable reference on score of feature.
    pub fn score_mut(&mut self) -> &mut str {
        return &mut self.score;
    }

    /// Get mutable reference on strand of feature.
    pub fn strand_mut(&mut self) -> &mut str {
        return &mut self.strand;
    }

    /// Get mutable reference on attributes of feature.
    pub fn attributes_mut(&mut self) -> &mut HashMap<String, String> {
        return &mut self.attributes;
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use io::Strand;
    use std::collections::HashMap;

    const GFF_FILE: &'static [u8] = b"P0A7B8\tUniProtKB\tInitiator methionine\t1\t1\t.\t.\t.\tNote=Removed;ID=test
P0A7B8\tUniProtKB\tChain\t2\t176\t50\t+\t.\tNote=ATP-dependent protease subunit HslV;ID=PRO_0000148105
";
    //required because HashMap iter on element randomly
    const GFF_FILE_NO_ATTRIB: &'static [u8] = b"P0A7B8\tUniProtKB\tInitiator methionine\t1\t1\t.\t.\t.\t
P0A7B8\tUniProtKB\tChain\t2\t176\t50\t+\t.\t
";

    #[test]
    fn test_reader() {
        let seqname = ["P0A7B8", "P0A7B8"];
        let source = ["UniProtKB", "UniProtKB"];
        let feature_type = ["Initiator methionine", "Chain"];
        let starts = [1, 2];
        let ends = [1, 176];
        let scores = [None, Some(50)];
        let strand = [None, Some(Strand::Forward)];
        let frame = [".", "."];
        let mut attributes = [HashMap::new(), HashMap::new()];
        attributes[0].insert("ID".to_owned(), "test".to_owned());
        attributes[0].insert("Note".to_owned(), "Removed".to_owned());
        attributes[1].insert("ID".to_owned(), "PRO_0000148105".to_owned());
        attributes[1].insert("Note".to_owned(), "ATP-dependent protease subunit HslV".to_owned());

        let mut reader = Reader::new(GFF_FILE);
        for (i, r) in reader.records().enumerate() {
            let record = r.ok().expect("Error reading record");
            assert_eq!(record.seqname(), seqname[i]);
            assert_eq!(record.source(), source[i]);
            assert_eq!(record.feature_type(), feature_type[i]);
            assert_eq!(*record.start(), starts[i]);
            assert_eq!(*record.end(), ends[i]);
            assert_eq!(record.score(), scores[i]);
            assert_eq!(record.strand(), strand[i]);
            assert_eq!(record.frame(), frame[i]);
            assert_eq!(record.attributes(), &attributes[i]);
        }
    }

    #[test]
    fn test_writer() {
        let mut reader = Reader::new(GFF_FILE_NO_ATTRIB);
        let mut writer = Writer::new(vec![]);
        for r in reader.records() {
            writer.write(r.ok().expect("Error reading record")).ok().expect("Error writing record");
        }
        assert_eq!(writer.inner.as_string(), String::from_utf8_lossy(GFF_FILE_NO_ATTRIB))
    }
}
<commit_msg>Get muttable reference of the original type is a better<commit_after>// Copyright 2016 Pierre Marijon.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! GFF3 format reading and writing.
//!
//! GFF2 definition : http://gmod.org/wiki/GFF2#The_GFF2_File_Format (not yet support)
//! GTF2 definition : http://mblab.wustl.edu/GTF2.html (not yet support)
//! GFF3 definition : http://gmod.org/wiki/GFF3#GFF3_Format
//!
//! # Example
//!
//! ```
//! use std::io;
//! use bio::io::gff;
//! let reader = gff::Reader::new(io::stdin());
//! ```

use std::io;
use std::fs;
use std::path::Path;
use std::convert::AsRef;
use std::collections::HashMap;

use csv;

use io::Strand;

/// A GFF reader.
pub struct Reader<R: io::Read> {
    inner: csv::Reader<R>,
}

impl Reader<fs::File> {
    /// Read GFF from given file path.
    pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        fs::File::open(path).map(Reader::new)
    }
}

impl<R: io::Read> Reader<R> {
    /// Create a new GFF reader given an instance of `io::Read`.
    pub fn new(reader: R) -> Self {
        Reader {
            inner: csv::Reader::from_reader(reader).delimiter(b'\t').has_headers(false)
        }
    }

    /// Iterate over all records.
    pub fn records(&mut self) -> Records<R> {
        Records { inner: self.inner.decode() }
    }
}

/// A GFF record.
pub struct Records<'a, R: 'a + io::Read> {
    inner: csv::DecodedRecords<'a, R, (String, String, String, u64, u64, String, String, String, String)>,
}

impl<'a, R: io::Read> Iterator for Records<'a, R> {
    type Item = csv::Result<Record>;

    fn next(&mut self) -> Option<csv::Result<Record>> {
        self.inner.next().map(|res| {
            res.map(|(seqname, source, feature_type, start, end, score, strand, frame, attributes)| {
                Record {
                    seqname: seqname,
                    source: source,
                    feature_type: feature_type,
                    start: start,
                    end: end,
                    score: score,
                    strand: strand,
                    frame: frame,
                    attributes: csv::Reader::from_string(attributes)
                        .delimiter(b'=')
                        .record_terminator(csv::RecordTerminator::Any(b';'))
                        .has_headers(false)
                        .decode().collect::<csv::Result<HashMap<String, String>>>().unwrap(),
                }
            })
        })
    }
}

/// A GFF writer.
pub struct Writer<W: io::Write> {
    inner: csv::Writer<W>,
}

impl Writer<fs::File> {
    /// Write to a given file path.
    pub fn to_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        fs::File::create(path).map(Writer::new)
    }
}

impl<W: io::Write> Writer<W> {
    /// Write to a given writer.
    pub fn new(writer: W) -> Self {
        Writer { inner: csv::Writer::from_writer(writer).delimiter(b'\t').flexible(true) }
    }

    /// Write a given GFF record.
    pub fn write(&mut self, record: Record) -> csv::Result<()> {
        let attributes;
        if !record.attributes.is_empty() {
            attributes = record.attributes.iter().map(|(a, b)| format!("{}={}", a, b)).collect::<Vec<_>>().join(";");
        } else {
            attributes = "".to_owned();
        }
        self.inner.encode((record.seqname, record.source, record.feature_type, record.start, record.end, record.score, record.strand, record.frame, attributes))
    }
}

/// A GFF record
#[derive(RustcEncodable)]
pub struct Record {
    seqname: String,
    source: String,
    feature_type: String,
    start: u64,
    end: u64,
    score: String,
    strand: String,
    frame: String,
    attributes: HashMap<String, String>,
}

impl Record {
    /// Create a new GFF record.
    pub fn new() -> Self {
        Record {
            seqname: "".to_owned(),
            source: "".to_owned(),
            feature_type: "".to_owned(),
            start: 0,
            end: 0,
            score: ".".to_owned(),
            strand: ".".to_owned(),
            frame: "".to_owned(),
            attributes: HashMap::<String, String>::new(),
        }
    }

    /// Sequence name of the feature.
    pub fn seqname(&self) -> &str {
        &self.seqname
    }

    /// Source of the feature.
    pub fn source(&self) -> &str {
        &self.source
    }

    /// Type of the feature.
    pub fn feature_type(&self) -> &str {
        &self.feature_type
    }

    /// Start position of feature (1-based).
    pub fn start(&self) -> &u64 {
        &self.start
    }

    /// End position of feature (1-based, not included).
    pub fn end(&self) -> &u64 {
        &self.end
    }

    /// Score of feature
    pub fn score(&self) -> Option<u64> {
        match self.score.as_ref() {
            "." => None,
            _ => self.score.parse::<u64>().ok(),
        }
    }

    /// Strand of the feature.
    pub fn strand(&self) -> Option<Strand> {
        match self.strand.as_ref() {
            "+" => Some(Strand::Forward),
            "-" => Some(Strand::Reverse),
            _ => None,
        }
    }

    /// Frame of the feature.
    pub fn frame(&self) -> &str {
        &self.frame
    }

    /// Attribute of feature
    pub fn attributes(&self) -> &HashMap<String, String> {
        &self.attributes
    }

    /// Get mutable reference on seqname of feature.
    pub fn seqname_mut(&mut self) -> &mut String {
        return &mut self.seqname;
    }

    /// Get mutable reference on source of feature.
    pub fn source_mut(&mut self) -> &mut String {
        return &mut self.source;
    }

    /// Get mutable reference on type of feature.
    pub fn feature_type_mut(&mut self) -> &mut String {
        return &mut self.feature_type;
    }

    /// Get mutable reference on start of feature.
    pub fn start_mut(&mut self) -> &mut u64 {
        return &mut self.start;
    }

    /// Get mutable reference on end of feature.
    pub fn end_mut(&mut self) -> &mut u64 {
        return &mut self.end;
    }

    /// Get mutable reference on score of feature.
    pub fn score_mut(&mut self) -> &mut String {
        return &mut self.score;
    }

    /// Get mutable reference on strand of feature.
    pub fn strand_mut(&mut self) -> &mut String {
        return &mut self.strand;
    }

    /// Get mutable reference on attributes of feature.
    pub fn attributes_mut(&mut self) -> &mut HashMap<String, String> {
        return &mut self.attributes;
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use io::Strand;
    use std::collections::HashMap;

    const GFF_FILE: &'static [u8] = b"P0A7B8\tUniProtKB\tInitiator methionine\t1\t1\t.\t.\t.\tNote=Removed;ID=test
P0A7B8\tUniProtKB\tChain\t2\t176\t50\t+\t.\tNote=ATP-dependent protease subunit HslV;ID=PRO_0000148105
";
    //required because HashMap iter on element randomly
    const GFF_FILE_NO_ATTRIB: &'static [u8] = b"P0A7B8\tUniProtKB\tInitiator methionine\t1\t1\t.\t.\t.\t
P0A7B8\tUniProtKB\tChain\t2\t176\t50\t+\t.\t
";

    #[test]
    fn test_reader() {
        let seqname = ["P0A7B8", "P0A7B8"];
        let source = ["UniProtKB", "UniProtKB"];
        let feature_type = ["Initiator methionine", "Chain"];
        let starts = [1, 2];
        let ends = [1, 176];
        let scores = [None, Some(50)];
        let strand = [None, Some(Strand::Forward)];
        let frame = [".", "."];
        let mut attributes = [HashMap::new(), HashMap::new()];
        attributes[0].insert("ID".to_owned(), "test".to_owned());
        attributes[0].insert("Note".to_owned(), "Removed".to_owned());
        attributes[1].insert("ID".to_owned(), "PRO_0000148105".to_owned());
        attributes[1].insert("Note".to_owned(), "ATP-dependent protease subunit HslV".to_owned());

        let mut reader = Reader::new(GFF_FILE);
        for (i, r) in reader.records().enumerate() {
            let record = r.ok().expect("Error reading record");
            assert_eq!(record.seqname(), seqname[i]);
            assert_eq!(record.source(), source[i]);
            assert_eq!(record.feature_type(), feature_type[i]);
            assert_eq!(*record.start(), starts[i]);
            assert_eq!(*record.end(), ends[i]);
            assert_eq!(record.score(), scores[i]);
            assert_eq!(record.strand(), strand[i]);
            assert_eq!(record.frame(), frame[i]);
            assert_eq!(record.attributes(), &attributes[i]);
        }
    }

    #[test]
    fn test_writer() {
        let mut reader = Reader::new(GFF_FILE_NO_ATTRIB);
        let mut writer = Writer::new(vec![]);
        for r in reader.records() {
            writer.write(r.ok().expect("Error reading record")).ok().expect("Error writing record");
        }
        assert_eq!(writer.inner.as_string(), String::from_utf8_lossy(GFF_FILE_NO_ATTRIB))
    }
}
<|endoftext|>


<commit_before>use std::io::Result;
use self::super::{Github, SortDirection, State};
use rep::{Issue, IssueReq, Label};
use rustc_serialize::json;
use std::fmt;
use std::default::Default;

pub enum Filter {
  Assigned,
  Created,
  Mentioned,
  Subscribed,
  All
}

impl fmt::Display for Filter {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", match *self {
      Filter::Assigned => "assigned",
      Filter::Created => "created",
      Filter::Mentioned => "mentioned",
      Filter::Subscribed => "subscribed",
      Filter::All => "all"
    })
  }
}

impl Default for Filter {
  fn default() -> Filter {
    Filter::Assigned
  }
}

pub enum Sort {
  Created,
  Updated,
  Comments
}

impl fmt::Display for Sort {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", match *self {
      Sort::Created => "created",
      Sort::Updated => "updated",
      Sort::Comments => "comments"
    })
  }
}

impl Default for Sort {
  fn default() -> Sort {
    Sort::Created
  }
}

pub struct IssueRef<'a> {
  github: &'a Github<'a>,
  owner: &'static str,
  repo: &'static str,
  number: &'static i64
}

impl<'a> IssueRef<'a> {
  /// create a new instance of a github repo issue ref
  pub fn new(
    github: &'a Github<'a>, owner: &'static str, repo: &'static str,
    number: &'static i64) -> IssueRef<'a> {
    IssueRef {
      github: github,
      owner: owner,
      repo: repo,
      number: number
    }
  }

  fn path(&self, more: &str) -> String {
    format!("/repos/{}/{}/issues/{}{}", self.owner, self.repo, self.number, more)
  }

  // add a set of labels to this issue ref
  pub fn label(&self, labels: Vec<&str>) -> Result<Vec<Label>> {
    let body = try!(self.github.post(
      &self.path("/labels"),
      json::encode(&labels).unwrap().as_bytes()
    ));
    Ok(json::decode::<Vec<Label>>(&body).unwrap())
  }
}

pub struct Issues<'a> {
  github: &'a Github<'a>,
  owner: &'static str,
  repo: &'static str
}

impl<'a> Issues<'a> {
  /// create a new instance of a github repo issue ref
  pub fn new(
    github: &'a Github<'a>, owner: &'static str, repo: &'static str) -> Issues<'a> {
    Issues {
      github: github,
      owner: owner,
      repo: repo
    }
  }

  fn path(&self, more: &str) -> String {
    format!("/repos/{}/{}/issues{}", self.owner, self.repo, more)
  }

  pub fn create(&self, is: &IssueReq) -> Result<Issue> {
    let data = json::encode(&is).unwrap();
    let body = try!(
      self.github.post(
        &self.path(""),
        data.as_bytes()
      )
    );
    Ok(json::decode::<Issue>(&body).unwrap())
  }

  pub fn list(
    &self,
    state: State,
    sort: Sort,
    direction: SortDirection,
    assignee: Option<&'static str>,
    creator: Option<&'static str>,
    mentioned: Option<&'static str>,
    labels: Vec<&'static str>,
    since: Option<String>
   ) -> Result<Vec<Issue>> {
    let mut params = Vec::new();
    params.push(format!("state={}", state));
    params.push(format!("sort={}", sort));
    params.push(format!("direction={}", direction));
    if let Some(a) = assignee {
      params.push(format!("assignee={}", a));
    }
    if let Some(c) = creator {
      params.push(format!("creator={}", c));
    }
    let url = self.path(
      &format!("?{}", params.join("&"))
    );
    let body = try!(
      self.github.get(
        &url
      )
    );
    Ok(json::decode::<Vec<Issue>>(&body).unwrap())
  }
}
<commit_msg>gists.get<commit_after>use std::io::Result;
use self::super::{Github, SortDirection, State};
use rep::{Issue, IssueReq, Label};
use rustc_serialize::json;
use std::fmt;
use std::default::Default;

pub enum Filter {
  Assigned,
  Created,
  Mentioned,
  Subscribed,
  All
}

impl fmt::Display for Filter {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", match *self {
      Filter::Assigned => "assigned",
      Filter::Created => "created",
      Filter::Mentioned => "mentioned",
      Filter::Subscribed => "subscribed",
      Filter::All => "all"
    })
  }
}

impl Default for Filter {
  fn default() -> Filter {
    Filter::Assigned
  }
}

pub enum Sort {
  Created,
  Updated,
  Comments
}

impl fmt::Display for Sort {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", match *self {
      Sort::Created => "created",
      Sort::Updated => "updated",
      Sort::Comments => "comments"
    })
  }
}

impl Default for Sort {
  fn default() -> Sort {
    Sort::Created
  }
}

pub struct IssueRef<'a> {
  github: &'a Github<'a>,
  owner: &'static str,
  repo: &'static str,
  number: &'static i64
}

impl<'a> IssueRef<'a> {
  /// create a new instance of a github repo issue ref
  pub fn new(
    github: &'a Github<'a>, owner: &'static str, repo: &'static str,
    number: &'static i64) -> IssueRef<'a> {
    IssueRef {
      github: github,
      owner: owner,
      repo: repo,
      number: number
    }
  }

  fn path(&self, more: &str) -> String {
    format!("/repos/{}/{}/issues/{}{}", self.owner, self.repo, self.number, more)
  }

  // add a set of labels to this issue ref
  pub fn label(&self, labels: Vec<&str>) -> Result<Vec<Label>> {
    let body = try!(self.github.post(
      &self.path("/labels"),
      json::encode(&labels).unwrap().as_bytes()
    ));
    Ok(json::decode::<Vec<Label>>(&body).unwrap())
  }
}

pub struct Issues<'a> {
  github: &'a Github<'a>,
  owner: &'static str,
  repo: &'static str
}

impl<'a> Issues<'a> {
  /// create a new instance of a github repo issue ref
  pub fn new(
    github: &'a Github<'a>, owner: &'static str, repo: &'static str) -> Issues<'a> {
    Issues {
      github: github,
      owner: owner,
      repo: repo
    }
  }

  fn path(&self, more: &str) -> String {
    format!("/repos/{}/{}/issues{}", self.owner, self.repo, more)
  }

  pub fn get(&self, number: &'static i64) -> IssueRef {
    IssueRef::new(self.github, self.owner, self.repo, number)
  }

  pub fn create(&self, is: &IssueReq) -> Result<Issue> {
    let data = json::encode(&is).unwrap();
    let body = try!(
      self.github.post(
        &self.path(""),
        data.as_bytes()
      )
    );
    Ok(json::decode::<Issue>(&body).unwrap())
  }

  pub fn list(
    &self,
    state: State,
    sort: Sort,
    direction: SortDirection,
    assignee: Option<&'static str>,
    creator: Option<&'static str>,
    mentioned: Option<&'static str>,
    labels: Vec<&'static str>,
    since: Option<String>
   ) -> Result<Vec<Issue>> {
    let mut params = Vec::new();
    params.push(format!("state={}", state));
    params.push(format!("sort={}", sort));
    params.push(format!("direction={}", direction));
    if let Some(a) = assignee {
      params.push(format!("assignee={}", a));
    }
    if let Some(c) = creator {
      params.push(format!("creator={}", c));
    }
    let url = self.path(
      &format!("?{}", params.join("&"))
    );
    let body = try!(
      self.github.get(
        &url
      )
    );
    Ok(json::decode::<Vec<Issue>>(&body).unwrap())
  }
}
<|endoftext|>


<commit_before>//! Issues interface

use self::super::{Github, Result, SortDirection, State};
use rep::{Issue, IssueReq, Label};
use rustc_serialize::json;
use std::fmt;
use std::default::Default;
use url::form_urlencoded;

pub enum Filter {
    Assigned,
    Created,
    Mentioned,
    Subscribed,
    All,
}

impl fmt::Display for Filter {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{}",
               match *self {
                   Filter::Assigned => "assigned",
                   Filter::Created => "created",
                   Filter::Mentioned => "mentioned",
                   Filter::Subscribed => "subscribed",
                   Filter::All => "all",
               })
    }
}

impl Default for Filter {
    fn default() -> Filter {
        Filter::Assigned
    }
}

#[derive(Clone)]
pub enum Sort {
    Created,
    Updated,
    Comments,
}

impl fmt::Display for Sort {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{}",
               match *self {
                   Sort::Created => "created",
                   Sort::Updated => "updated",
                   Sort::Comments => "comments",
               })
    }
}

impl Default for Sort {
    fn default() -> Sort {
        Sort::Created
    }
}

pub struct IssueLabels<'a> {
    github: &'a Github<'a>,
    owner: String,
    repo: String,
    number: u64,
}

impl<'a> IssueLabels<'a> {
    pub fn new<O, R>(github: &'a Github<'a>, owner: O, repo: R, number: u64) -> IssueLabels<'a>
        where O: Into<String>,
              R: Into<String>
    {
        IssueLabels {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
            number: number,
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/issues/{}/labels{}",
                self.owner,
                self.repo,
                self.number,
                more)
    }

    /// add a set of labels to this issue ref
    pub fn add(&self, labels: Vec<&str>) -> Result<Vec<Label>> {
        self.github
            .post::<Vec<Label>>(&self.path(""), try!(json::encode(&labels)).as_bytes())
    }

    /// remove a label from this issue
    pub fn remove(&self, label: &str) -> Result<()> {
        self.github
            .delete(&self.path(&format!("/{}", label)))
    }

    /// replace all labels associated with this issue with a new set.
    /// providing an empty set of labels is the same as clearing the
    /// current labels
    pub fn set(&self, labels: Vec<&str>) -> Result<Vec<Label>> {
        self.github.put::<Vec<Label>>(&self.path(""), try!(json::encode(&labels)).as_bytes())
    }

    /// remove all labels from an issue
    pub fn clear(&self) -> Result<()> {
        self.github
            .delete(&self.path(""))
    }
}

pub struct IssueRef<'a> {
    github: &'a Github<'a>,
    owner: String,
    repo: String,
    number: u64,
}

impl<'a> IssueRef<'a> {
    /// create a new instance of a github repo issue ref
    pub fn new<O, R>(github: &'a Github<'a>, owner: O, repo: R, number: u64) -> IssueRef<'a>
        where O: Into<String>,
              R: Into<String>
    {
        IssueRef {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
            number: number,
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/issues/{}{}",
                self.owner,
                self.repo,
                self.number,
                more)
    }

    pub fn labels(&self) -> IssueLabels {
        IssueLabels::new(self.github,
                         self.owner.as_ref(),
                         self.repo.as_ref(),
                         self.number)
    }

    pub fn edit(&self, is: &IssueReq) -> Result<Issue> {
        let data = try!(json::encode(&is));
        self.github.patch::<Issue>(&self.path(""), data.as_bytes())
    }
}

pub struct Issues<'a> {
    github: &'a Github<'a>,
    owner: String,
    repo: String,
}

pub struct ListReq {
    state: State,
    sort: Sort,
    direction: SortDirection,
    assignee: Option<String>,
    creator: Option<String>,
    mentioned: Option<String>,
    labels: Vec<String>,
    since: Option<String>,
}

impl ListReq {
    pub fn builder() -> ListReqBuilder {
        ListReqBuilder::new()
    }
}

/// an mutable issue list builder
#[derive(Default)]
pub struct ListReqBuilder {
    state: State,
    sort: Sort,
    direction: SortDirection,
    assignee: Option<String>,
    creator: Option<String>,
    mentioned: Option<String>,
    labels: Vec<String>,
    since: Option<String>,
}

impl ListReqBuilder {

    pub fn new() -> ListReqBuilder {
        ListReqBuilder {
            ..Default::default()
        }
    }

    pub fn state(&mut self, state: State) -> &mut ListReqBuilder {
        self.state = state;
        self
    }

    pub fn sort(&mut self, sort: Sort) -> &mut ListReqBuilder {
        self.sort = sort;
        self
    }

    pub fn direction(&mut self, direction: SortDirection) -> &mut ListReqBuilder {
        self.direction = direction;
        self
    }

    pub fn assignee<A>(&mut self, assignee: A) -> &mut ListReqBuilder where A: Into<String> {
        self.assignee = Some(assignee.into());
        self
    }

    pub fn creator<C>(&mut self, creator: C) -> &mut ListReqBuilder where C: Into<String> {
        self.creator = Some(creator.into());
        self
    }

    pub fn mentioned<M>(&mut self, mentioned: M) -> &mut ListReqBuilder where M: Into<String> {
        self.mentioned = Some(mentioned.into());
        self
    }

    pub fn labels<L>(&mut self, labels: Vec<L>) -> &mut ListReqBuilder where L: Into<String> {
        self.labels = labels.into_iter().map(|l|l.into()).collect::<Vec<String>>();
        self
    }

    pub fn since<S>(&mut self, since: S) -> &mut ListReqBuilder where S: Into<String> {
        self.since = Some(since.into());
        self
    }

    pub fn build(&self) -> ListReq {
        ListReq {
            state: self.state.clone(),
            sort: self.sort.clone(),
            direction: self.direction.clone(),
            assignee: self.assignee.clone(),
            creator: self.creator.clone(),
            mentioned: self.mentioned.clone(),
            labels: self.labels.clone(),
            since: self.since.clone(),
        }
    }

}

impl<'a> Issues<'a> {
    /// create a new instance of a github repo issue ref
    pub fn new<O, R>(github: &'a Github<'a>, owner: O, repo: R) -> Issues<'a>
        where O: Into<String>,
              R: Into<String>
    {
        Issues {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/issues{}", self.owner, self.repo, more)
    }

    pub fn get(&self, number: u64) -> IssueRef {
        IssueRef::new(self.github, self.owner.as_ref(), self.repo.as_ref(), number)
    }

    pub fn create(&self, is: &IssueReq) -> Result<Issue> {
        let data = try!(json::encode(&is));
        self.github.post::<Issue>(&self.path(""), data.as_bytes())
    }

    pub fn list(&self, req: &ListReq) -> Result<Vec<Issue>> {
        let mut params = Vec::new();
        params.push(("state", req.state.to_string()));
        params.push(("sort", req.sort.to_string()));
        params.push(("direction", req.direction.to_string()));
        if let Some(ref a) = req.assignee {
            params.push(("assignee", a.to_owned()));
        }
        if let Some(ref c) = req.creator {
            params.push(("creator", c.to_owned()));
        }
        if let Some(ref m) = req.mentioned {
            params.push(("mentioned", m.to_owned()));
        }
        if let Some(ref s) = req.since {
            params.push(("since", s.to_owned()));
        }
        if !req.labels.is_empty() {
            params.push(("labels", req.labels.connect(",")));
        }
        let url = self.path(&format!("?{}", form_urlencoded::serialize(params)));
        self.github.get::<Vec<Issue>>(&url)
    }
}
<commit_msg>make issue list req serialize testable outside execution path<commit_after>//! Issues interface

use self::super::{Github, Result, SortDirection, State};
use rep::{Issue, IssueReq, Label};
use rustc_serialize::json;
use std::fmt;
use std::default::Default;
use url::form_urlencoded;

pub enum Filter {
    Assigned,
    Created,
    Mentioned,
    Subscribed,
    All,
}

impl fmt::Display for Filter {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{}",
               match *self {
                   Filter::Assigned => "assigned",
                   Filter::Created => "created",
                   Filter::Mentioned => "mentioned",
                   Filter::Subscribed => "subscribed",
                   Filter::All => "all",
               })
    }
}

impl Default for Filter {
    fn default() -> Filter {
        Filter::Assigned
    }
}

#[derive(Clone)]
pub enum Sort {
    Created,
    Updated,
    Comments,
}

impl fmt::Display for Sort {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{}",
               match *self {
                   Sort::Created => "created",
                   Sort::Updated => "updated",
                   Sort::Comments => "comments",
               })
    }
}

impl Default for Sort {
    fn default() -> Sort {
        Sort::Created
    }
}

pub struct IssueLabels<'a> {
    github: &'a Github<'a>,
    owner: String,
    repo: String,
    number: u64,
}

impl<'a> IssueLabels<'a> {
    pub fn new<O, R>(github: &'a Github<'a>, owner: O, repo: R, number: u64) -> IssueLabels<'a>
        where O: Into<String>,
              R: Into<String>
    {
        IssueLabels {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
            number: number,
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/issues/{}/labels{}",
                self.owner,
                self.repo,
                self.number,
                more)
    }

    /// add a set of labels to this issue ref
    pub fn add(&self, labels: Vec<&str>) -> Result<Vec<Label>> {
        self.github
            .post::<Vec<Label>>(&self.path(""), try!(json::encode(&labels)).as_bytes())
    }

    /// remove a label from this issue
    pub fn remove(&self, label: &str) -> Result<()> {
        self.github
            .delete(&self.path(&format!("/{}", label)))
    }

    /// replace all labels associated with this issue with a new set.
    /// providing an empty set of labels is the same as clearing the
    /// current labels
    pub fn set(&self, labels: Vec<&str>) -> Result<Vec<Label>> {
        self.github.put::<Vec<Label>>(&self.path(""), try!(json::encode(&labels)).as_bytes())
    }

    /// remove all labels from an issue
    pub fn clear(&self) -> Result<()> {
        self.github
            .delete(&self.path(""))
    }
}

pub struct IssueRef<'a> {
    github: &'a Github<'a>,
    owner: String,
    repo: String,
    number: u64,
}

impl<'a> IssueRef<'a> {
    /// create a new instance of a github repo issue ref
    pub fn new<O, R>(github: &'a Github<'a>, owner: O, repo: R, number: u64) -> IssueRef<'a>
        where O: Into<String>,
              R: Into<String>
    {
        IssueRef {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
            number: number,
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/issues/{}{}",
                self.owner,
                self.repo,
                self.number,
                more)
    }

    pub fn labels(&self) -> IssueLabels {
        IssueLabels::new(self.github,
                         self.owner.as_ref(),
                         self.repo.as_ref(),
                         self.number)
    }

    pub fn edit(&self, is: &IssueReq) -> Result<Issue> {
        let data = try!(json::encode(&is));
        self.github.patch::<Issue>(&self.path(""), data.as_bytes())
    }
}

pub struct Issues<'a> {
    github: &'a Github<'a>,
    owner: String,
    repo: String,
}

pub struct ListReq {
    state: State,
    sort: Sort,
    direction: SortDirection,
    assignee: Option<String>,
    creator: Option<String>,
    mentioned: Option<String>,
    labels: Vec<String>,
    since: Option<String>,
}

impl ListReq {
    pub fn builder() -> ListReqBuilder {
        ListReqBuilder::new()
    }

    pub fn serialize(&self) -> String {
        let mut params = Vec::new();
        params.push(("state", self.state.to_string()));
        params.push(("sort", self.sort.to_string()));
        params.push(("direction", self.direction.to_string()));
        if let Some(ref a) = self.assignee {
            params.push(("assignee", a.to_owned()));
        }
        if let Some(ref c) = self.creator {
            params.push(("creator", c.to_owned()));
        }
        if let Some(ref m) = self.mentioned {
            params.push(("mentioned", m.to_owned()));
        }
        if let Some(ref s) = self.since {
            params.push(("since", s.to_owned()));
        }
        if !self.labels.is_empty() {
            params.push(("labels", self.labels.connect(",")));
        }
        form_urlencoded::serialize(params)
    }
}

/// an mutable issue list builder
#[derive(Default)]
pub struct ListReqBuilder {
    state: State,
    sort: Sort,
    direction: SortDirection,
    assignee: Option<String>,
    creator: Option<String>,
    mentioned: Option<String>,
    labels: Vec<String>,
    since: Option<String>,
}

impl ListReqBuilder {

    pub fn new() -> ListReqBuilder {
        ListReqBuilder {
            ..Default::default()
        }
    }

    pub fn state(&mut self, state: State) -> &mut ListReqBuilder {
        self.state = state;
        self
    }

    pub fn sort(&mut self, sort: Sort) -> &mut ListReqBuilder {
        self.sort = sort;
        self
    }

    pub fn direction(&mut self, direction: SortDirection) -> &mut ListReqBuilder {
        self.direction = direction;
        self
    }

    pub fn assignee<A>(&mut self, assignee: A) -> &mut ListReqBuilder where A: Into<String> {
        self.assignee = Some(assignee.into());
        self
    }

    pub fn creator<C>(&mut self, creator: C) -> &mut ListReqBuilder where C: Into<String> {
        self.creator = Some(creator.into());
        self
    }

    pub fn mentioned<M>(&mut self, mentioned: M) -> &mut ListReqBuilder where M: Into<String> {
        self.mentioned = Some(mentioned.into());
        self
    }

    pub fn labels<L>(&mut self, labels: Vec<L>) -> &mut ListReqBuilder where L: Into<String> {
        self.labels = labels.into_iter().map(|l|l.into()).collect::<Vec<String>>();
        self
    }

    pub fn since<S>(&mut self, since: S) -> &mut ListReqBuilder where S: Into<String> {
        self.since = Some(since.into());
        self
    }

    pub fn build(&self) -> ListReq {
        ListReq {
            state: self.state.clone(),
            sort: self.sort.clone(),
            direction: self.direction.clone(),
            assignee: self.assignee.clone(),
            creator: self.creator.clone(),
            mentioned: self.mentioned.clone(),
            labels: self.labels.clone(),
            since: self.since.clone(),
        }
    }

}

impl<'a> Issues<'a> {
    /// create a new instance of a github repo issue ref
    pub fn new<O, R>(github: &'a Github<'a>, owner: O, repo: R) -> Issues<'a>
        where O: Into<String>,
              R: Into<String>
    {
        Issues {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/issues{}", self.owner, self.repo, more)
    }

    pub fn get(&self, number: u64) -> IssueRef {
        IssueRef::new(self.github, self.owner.as_ref(), self.repo.as_ref(), number)
    }

    pub fn create(&self, is: &IssueReq) -> Result<Issue> {
        let data = try!(json::encode(&is));
        self.github.post::<Issue>(&self.path(""), data.as_bytes())
    }

    pub fn list(&self, req: &ListReq) -> Result<Vec<Issue>> {
        let url = self.path(&format!("?{}", req.serialize()));
        self.github.get::<Vec<Issue>>(&url)
    }
}

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

    #[test]
    fn list_reqs() {
        fn test_serialize(tests: Vec<(ListReq, &str)>) {
            for test in tests {
                match test {
                    (k, v) => assert_eq!(k.serialize(), v)
                }
            }
        }
        let tests = vec![
            (
                ListReq::builder().build(),
                "state=open&sort=created&direction=asc"
            ),
            (
                ListReq::builder().state(State::Closed).build(),
                "state=closed&sort=created&direction=asc"
             ),
            (
                ListReq::builder().labels(vec!["foo", "bar"]).build(),
                "state=open&sort=created&direction=asc&labels=foo%2Cbar"
            ),
        ];
        test_serialize(tests)
    }
    #[test]
    fn filter_display() {
        for (k,v) in vec![
            (Filter::Assigned, "assigned"),
            (Filter::Created, "created"),
            (Filter::Mentioned, "mentioned"),
            (Filter::Subscribed, "subscribed"),
            (Filter::All, "all"),
        ] {
            assert_eq!(k.to_string(), v)
        }
    }
}
<|endoftext|>


<commit_before>use {Euclid, Point};
use point::Euclidean;

/// Clustering via the *k*-means algorithm (aka Lloyd's algorithm).
///
/// > *k*-means clustering aims to partition *n* observations into *k*
/// clusters in which each observation belongs to the cluster with the
/// nearest mean, serving as a prototype of the cluster.wikipedia
///
/// This is a heuristic, iterative approximation to the true optimal
/// assignment. The parameters used to control the approximation can
/// be set via `KmeansBuilder`.
///
/// # Examples
///
/// ```rust
/// use cogset::{Euclid, Kmeans};
///
/// let data = [Euclid([0.0, 0.0]),
///             Euclid([1.0, 0.5]),
///             Euclid([0.2, 0.2]),
///             Euclid([0.3, 0.8]),
///             Euclid([0.0, 1.0])];
/// let k = 3;
///
/// let kmeans = Kmeans::new(&data, k);
///
/// println!("{:?}", kmeans.clusters());
/// ```

pub struct Kmeans<T> {
    assignments: Vec<usize>,
    centres: Vec<Euclid<T>>,
    iterations: usize,
    converged: bool,
}

impl<T> Kmeans<T>
    where Euclid<T>: Point + Euclidean + Clone
{
    /// Run k-means on `data` with the default settings.
    pub fn new(data: &[Euclid<T>], k: usize) -> Kmeans<T> {
        KmeansBuilder::new().kmeans(data, k)
    }

    /// Retrieve the means and the clusters themselves that this
    /// *k*-means instance computed.
    ///
    /// The clusters are represented by vectors of indexes into the
    /// original data.
    pub fn clusters(&self) -> Vec<(Euclid<T>, Vec<usize>)> {
        let mut ret = self.centres.iter().cloned().map(|c| (c, vec![])).collect::<Vec<_>>();

        for (idx, &assign) in self.assignments.iter().enumerate() {
            ret[assign].1.push(idx);
        }

        ret
    }

    /// Return whether the algorithm converged, and how many steps
    /// that took.
    ///
    /// `Ok` is returned if the algorithm did meet the tolerance
    /// criterion, and `Err` if it reached the iteration limit
    /// instead.
    pub fn converged(&self) -> Result<usize, usize> {
        if self.converged {
            Ok(self.iterations)
        } else {
            Err(self.iterations)
        }
    }
}

const DEFAULT_MAX_ITER: usize = 100;
const DEFAULT_TOL: f64 = 1e-6;

/// A builder for *k*-means to provide control over parameters for the
/// algorithm.
///
/// This allows one to tweak settings like the tolerance and the
/// number of iterations.
///
/// # Examples
///
/// ```rust
/// use cogset::{Euclid, KmeansBuilder};
///
/// let data = [Euclid([0.0, 0.0]),
///             Euclid([1.0, 0.5]),
///             Euclid([0.2, 0.2]),
///             Euclid([0.3, 0.8]),
///             Euclid([0.0, 1.0])];
///
/// let k = 3;
///
/// // we want the means extra precise.
/// let tol = 1e-12;
/// let kmeans = KmeansBuilder::new().tolerance(tol).kmeans(&data, k);
///
/// println!("{:?}", kmeans.clusters());
/// ```
pub struct KmeansBuilder {
    tol: f64,
    max_iter: usize,
}

impl KmeansBuilder {
    /// Create a default `KmeansBuilder`
    pub fn new() -> KmeansBuilder {
        KmeansBuilder {
            tol: DEFAULT_TOL,
            max_iter: DEFAULT_MAX_ITER,
        }
    }

    /// Set the tolerance used to decide if the iteration has
    /// converged to `tol`.
    pub fn tolerance(self, tol: f64) -> KmeansBuilder {
        KmeansBuilder { tol: tol, .. self }
    }
    /// Set the maximum number of iterations to run before aborting to
    /// `max_iter`.
    pub fn max_iter(self, max_iter: usize) -> KmeansBuilder {
        KmeansBuilder { max_iter: max_iter, .. self }
    }

    /// Run *k*-means with the given settings.
    ///
    /// This is functionally identical to `Kmeans::new`, other than
    /// the internal parameters differing.
    pub fn kmeans<T>(self, data: &[Euclid<T>], k: usize) -> Kmeans<T>
        where Euclid<T>: Point + Euclidean + Clone
    {
        assert!(2 <= k && k < data.len());

        let n = data.len();
        let mut assignments = vec![!0; n];
        let mut costs = vec![0.0; n];
        let mut centres = data.iter().take(k).cloned().collect::<Vec<_>>();
        let mut counts = vec![0; k];
        update_assignments(data, &mut assignments, &mut counts, &mut costs, &centres);
        let mut objective = costs.iter().fold(0.0, |a, b| a + *b);

        let mut converged = false;
        let mut iter = 0;
        while iter < self.max_iter {
            update_centres(data, &assignments, &counts, &mut centres);
            update_assignments(data, &mut assignments, &mut counts, &mut costs, &centres);

            let new_objective = costs.iter().fold(0.0, |a, b| a + *b);

            if (new_objective - objective).abs() < self.tol {
                converged = true;
                break
            }

            objective = new_objective;
            iter += 1
        }

        Kmeans {
            assignments: assignments,
            centres: centres,
            iterations: iter,
            converged: converged,
        }
    }
}

fn update_assignments<T>(data: &[Euclid<T>],
                         assignments: &mut [usize], counts: &mut [usize], costs: &mut [f64],
                         centres: &[Euclid<T>])
    where Euclid<T>: Point + Euclidean + Clone
{
    use std::f64::INFINITY as INF;

    for place in counts.iter_mut() { *place = 0 }

    for ((point, assign), cost) in data.iter().zip(assignments.iter_mut()).zip(costs.iter_mut()) {
        let mut min_dist = INF;
        let mut index = 0;
        for (i, c) in centres.iter().enumerate() {
            // we can use the square of the distance here
            let dist = point.dist2(c);
            if dist < min_dist {
                min_dist = dist;
                index = i;
            }
        }

        // but we need to make sure we're storing the right cost.
        *cost = min_dist.sqrt();
        *assign = index;
        counts[index] += 1;
    }
}

fn update_centres<T>(data: &[Euclid<T>],
                     assignments: &[usize], counts: &[usize],
                     centres: &mut [Euclid<T>])
    where Euclid<T>: Point + Euclidean + Clone
{
    for place in centres.iter_mut() { *place = <Euclid<T>>::zero() }

    for (point, assign) in data.iter().zip(assignments.iter()) {
        centres[*assign].add(point)
    }
    for (place, scale) in centres.iter_mut().zip(counts.iter()) {
        place.scale(1.0 / *scale as f64)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use Euclid;
    use rand::{XorShiftRng,Rng};

    #[test]
    fn smoke() {
        let points = [Euclid([0.0, 0.0]),
                      Euclid([1.0, 0.5]),
                      Euclid([0.2, 0.2]),
                      Euclid([0.3, 0.8]),
                      Euclid([0.0, 1.0]),
                      ];

        let res = Kmeans::new(&points, 3);
        let mut clusters = res.clusters();
        for &mut (_, ref mut v) in &mut clusters {
            v.sort()
        }
        clusters.sort_by(|a, b| a.1.cmp(&b.1));
        assert_eq!(clusters,
                   [(Euclid([0.1, 0.1]), vec![0, 2]),
                    (Euclid([1.0, 0.5]), vec![1]),
                    (Euclid([0.15, 0.9]), vec![3, 4])]);

        assert_eq!(res.converged(), Ok(2));
    }
    #[test]
    fn no_converge() {
        let mut rng = XorShiftRng::new_unseeded();
        let points = (0..100)
            .map(|_| Euclid([rng.gen()]))
            .collect::<Vec<Euclid<[f64; 1]>>>();

        let res = KmeansBuilder::new().tolerance(1e-18).max_iter(2).kmeans(&points, 4);

        assert_eq!(res.converged(), Err(2))
    }
}

#[cfg(all(test, feature = "unstable"))]
mod benches {
    use super::Kmeans;
    use Euclid;
    use rand::{XorShiftRng,Rng};
    use test::Bencher;
    macro_rules! gen {
        ($r: expr, 1) => {
            [$r.gen::<f64>()]
        };
        ($r: expr, 2) => {
            [$r.gen::<f64>(), $r.gen::<f64>()]
        };
        ($r: expr, 3) => {
            [$r.gen::<f64>(), $r.gen::<f64>(), $r.gen::<f64>()]
        };
    }
    macro_rules! benches {
        ($($name: ident, $d: tt, $k: expr, $n: expr;)*) => {
            $(
                #[bench]
                fn $name(b: &mut Bencher) {
                    let k = $k;
                    let n = $n;

                    let mut rng = XorShiftRng::new_unseeded();
                    let points = (0..n)
                        .map(|_| Euclid(gen!(rng, $d)))
                        .collect::<Vec<_>>();

                    b.iter(|| Kmeans::new(&points, k))
                }
                )*
        }
    }

    benches! {
        d1_k003_n00010, 1,   3,    10;
        d1_k003_n00100, 1,   3,   100;
        d1_k003_n01000, 1,   3,  1000;
        d1_k003_n10000, 1,   3, 10000;

        d1_k010_n00100, 1,  10,   100;
        d1_k010_n01000, 1,  10,  1000;
        d1_k010_n10000, 1,  10, 10000;

        d1_k100_n01000, 1, 100,  1000;
        // too slow:
        // d1_k100_n10000, 1, 100, 10000;

        d2_k003_n00010, 2,   3,    10;
        d2_k003_n00100, 2,   3,   100;
        d2_k003_n01000, 2,   3,  1000;
        d2_k003_n10000, 2,   3, 10000;

        d2_k010_n00100, 2,  10,   100;
        d2_k010_n01000, 2,  10,  1000;
        d2_k010_n10000, 2,  10, 10000;

        d2_k100_n01000, 2, 100,  1000;
        // too slow:
        // d2_k100_n10000, 2, 100, 10000;

        d3_k003_n00010, 3,   3,    10;
        d3_k003_n00100, 3,   3,   100;
        d3_k003_n01000, 3,   3,  1000;
        d3_k003_n10000, 3,   3, 10000;

        d3_k010_n00100, 3,  10,   100;
        d3_k010_n01000, 3,  10,  1000;
        d3_k010_n10000, 3,  10, 10000;

        d3_k100_n01000, 3, 100,  1000;
        // too slow:
        // d3_k100_n10000, 3, 100, 10000;
    }
}
<commit_msg>kmeans: add 9-d benchmarks.<commit_after>use {Euclid, Point};
use point::Euclidean;

/// Clustering via the *k*-means algorithm (aka Lloyd's algorithm).
///
/// > *k*-means clustering aims to partition *n* observations into *k*
/// clusters in which each observation belongs to the cluster with the
/// nearest mean, serving as a prototype of the cluster.wikipedia
///
/// This is a heuristic, iterative approximation to the true optimal
/// assignment. The parameters used to control the approximation can
/// be set via `KmeansBuilder`.
///
/// # Examples
///
/// ```rust
/// use cogset::{Euclid, Kmeans};
///
/// let data = [Euclid([0.0, 0.0]),
///             Euclid([1.0, 0.5]),
///             Euclid([0.2, 0.2]),
///             Euclid([0.3, 0.8]),
///             Euclid([0.0, 1.0])];
/// let k = 3;
///
/// let kmeans = Kmeans::new(&data, k);
///
/// println!("{:?}", kmeans.clusters());
/// ```

pub struct Kmeans<T> {
    assignments: Vec<usize>,
    centres: Vec<Euclid<T>>,
    iterations: usize,
    converged: bool,
}

impl<T> Kmeans<T>
    where Euclid<T>: Point + Euclidean + Clone
{
    /// Run k-means on `data` with the default settings.
    pub fn new(data: &[Euclid<T>], k: usize) -> Kmeans<T> {
        KmeansBuilder::new().kmeans(data, k)
    }

    /// Retrieve the means and the clusters themselves that this
    /// *k*-means instance computed.
    ///
    /// The clusters are represented by vectors of indexes into the
    /// original data.
    pub fn clusters(&self) -> Vec<(Euclid<T>, Vec<usize>)> {
        let mut ret = self.centres.iter().cloned().map(|c| (c, vec![])).collect::<Vec<_>>();

        for (idx, &assign) in self.assignments.iter().enumerate() {
            ret[assign].1.push(idx);
        }

        ret
    }

    /// Return whether the algorithm converged, and how many steps
    /// that took.
    ///
    /// `Ok` is returned if the algorithm did meet the tolerance
    /// criterion, and `Err` if it reached the iteration limit
    /// instead.
    pub fn converged(&self) -> Result<usize, usize> {
        if self.converged {
            Ok(self.iterations)
        } else {
            Err(self.iterations)
        }
    }
}

const DEFAULT_MAX_ITER: usize = 100;
const DEFAULT_TOL: f64 = 1e-6;

/// A builder for *k*-means to provide control over parameters for the
/// algorithm.
///
/// This allows one to tweak settings like the tolerance and the
/// number of iterations.
///
/// # Examples
///
/// ```rust
/// use cogset::{Euclid, KmeansBuilder};
///
/// let data = [Euclid([0.0, 0.0]),
///             Euclid([1.0, 0.5]),
///             Euclid([0.2, 0.2]),
///             Euclid([0.3, 0.8]),
///             Euclid([0.0, 1.0])];
///
/// let k = 3;
///
/// // we want the means extra precise.
/// let tol = 1e-12;
/// let kmeans = KmeansBuilder::new().tolerance(tol).kmeans(&data, k);
///
/// println!("{:?}", kmeans.clusters());
/// ```
pub struct KmeansBuilder {
    tol: f64,
    max_iter: usize,
}

impl KmeansBuilder {
    /// Create a default `KmeansBuilder`
    pub fn new() -> KmeansBuilder {
        KmeansBuilder {
            tol: DEFAULT_TOL,
            max_iter: DEFAULT_MAX_ITER,
        }
    }

    /// Set the tolerance used to decide if the iteration has
    /// converged to `tol`.
    pub fn tolerance(self, tol: f64) -> KmeansBuilder {
        KmeansBuilder { tol: tol, .. self }
    }
    /// Set the maximum number of iterations to run before aborting to
    /// `max_iter`.
    pub fn max_iter(self, max_iter: usize) -> KmeansBuilder {
        KmeansBuilder { max_iter: max_iter, .. self }
    }

    /// Run *k*-means with the given settings.
    ///
    /// This is functionally identical to `Kmeans::new`, other than
    /// the internal parameters differing.
    pub fn kmeans<T>(self, data: &[Euclid<T>], k: usize) -> Kmeans<T>
        where Euclid<T>: Point + Euclidean + Clone
    {
        assert!(2 <= k && k < data.len());

        let n = data.len();
        let mut assignments = vec![!0; n];
        let mut costs = vec![0.0; n];
        let mut centres = data.iter().take(k).cloned().collect::<Vec<_>>();
        let mut counts = vec![0; k];
        update_assignments(data, &mut assignments, &mut counts, &mut costs, &centres);
        let mut objective = costs.iter().fold(0.0, |a, b| a + *b);

        let mut converged = false;
        let mut iter = 0;
        while iter < self.max_iter {
            update_centres(data, &assignments, &counts, &mut centres);
            update_assignments(data, &mut assignments, &mut counts, &mut costs, &centres);

            let new_objective = costs.iter().fold(0.0, |a, b| a + *b);

            if (new_objective - objective).abs() < self.tol {
                converged = true;
                break
            }

            objective = new_objective;
            iter += 1
        }

        Kmeans {
            assignments: assignments,
            centres: centres,
            iterations: iter,
            converged: converged,
        }
    }
}

fn update_assignments<T>(data: &[Euclid<T>],
                         assignments: &mut [usize], counts: &mut [usize], costs: &mut [f64],
                         centres: &[Euclid<T>])
    where Euclid<T>: Point + Euclidean + Clone
{
    use std::f64::INFINITY as INF;

    for place in counts.iter_mut() { *place = 0 }

    for ((point, assign), cost) in data.iter().zip(assignments.iter_mut()).zip(costs.iter_mut()) {
        let mut min_dist = INF;
        let mut index = 0;
        for (i, c) in centres.iter().enumerate() {
            // we can use the square of the distance here
            let dist = point.dist2(c);
            if dist < min_dist {
                min_dist = dist;
                index = i;
            }
        }

        // but we need to make sure we're storing the right cost.
        *cost = min_dist.sqrt();
        *assign = index;
        counts[index] += 1;
    }
}

fn update_centres<T>(data: &[Euclid<T>],
                     assignments: &[usize], counts: &[usize],
                     centres: &mut [Euclid<T>])
    where Euclid<T>: Point + Euclidean + Clone
{
    for place in centres.iter_mut() { *place = <Euclid<T>>::zero() }

    for (point, assign) in data.iter().zip(assignments.iter()) {
        centres[*assign].add(point)
    }
    for (place, scale) in centres.iter_mut().zip(counts.iter()) {
        place.scale(1.0 / *scale as f64)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use Euclid;
    use rand::{XorShiftRng,Rng};

    #[test]
    fn smoke() {
        let points = [Euclid([0.0, 0.0]),
                      Euclid([1.0, 0.5]),
                      Euclid([0.2, 0.2]),
                      Euclid([0.3, 0.8]),
                      Euclid([0.0, 1.0]),
                      ];

        let res = Kmeans::new(&points, 3);
        let mut clusters = res.clusters();
        for &mut (_, ref mut v) in &mut clusters {
            v.sort()
        }
        clusters.sort_by(|a, b| a.1.cmp(&b.1));
        assert_eq!(clusters,
                   [(Euclid([0.1, 0.1]), vec![0, 2]),
                    (Euclid([1.0, 0.5]), vec![1]),
                    (Euclid([0.15, 0.9]), vec![3, 4])]);

        assert_eq!(res.converged(), Ok(2));
    }
    #[test]
    fn no_converge() {
        let mut rng = XorShiftRng::new_unseeded();
        let points = (0..100)
            .map(|_| Euclid([rng.gen()]))
            .collect::<Vec<Euclid<[f64; 1]>>>();

        let res = KmeansBuilder::new().tolerance(1e-18).max_iter(2).kmeans(&points, 4);

        assert_eq!(res.converged(), Err(2))
    }
}

#[cfg(all(test, feature = "unstable"))]
mod benches {
    use super::Kmeans;
    use Euclid;
    use rand::{XorShiftRng,Rng};
    use test::Bencher;
    macro_rules! gen {
        ($r: expr, 1) => {
            [$r.gen::<f64>()]
        };
        ($r: expr, 2) => {
            [$r.gen::<f64>(), $r.gen::<f64>()]
        };
        ($r: expr, 3) => {
            [$r.gen::<f64>(), $r.gen::<f64>(), $r.gen::<f64>()]
        };
        ($r: expr, 9) => {
            [$r.gen::<f64>(), $r.gen::<f64>(), $r.gen::<f64>(),
             $r.gen::<f64>(), $r.gen::<f64>(), $r.gen::<f64>(),
             $r.gen::<f64>(), $r.gen::<f64>(), $r.gen::<f64>()]
        };
    }
    macro_rules! benches {
        ($($name: ident, $d: tt, $k: expr, $n: expr;)*) => {
            $(
                #[bench]
                fn $name(b: &mut Bencher) {
                    let k = $k;
                    let n = $n;

                    let mut rng = XorShiftRng::new_unseeded();
                    let points = (0..n)
                        .map(|_| Euclid(gen!(rng, $d)))
                        .collect::<Vec<_>>();

                    b.iter(|| Kmeans::new(&points, k))
                }
                )*
        }
    }

    benches! {
        d1_k003_n00010, 1,   3,    10;
        d1_k003_n00100, 1,   3,   100;
        d1_k003_n01000, 1,   3,  1000;
        d1_k003_n10000, 1,   3, 10000;

        d1_k010_n00100, 1,  10,   100;
        d1_k010_n01000, 1,  10,  1000;
        d1_k010_n10000, 1,  10, 10000;

        d1_k100_n01000, 1, 100,  1000;
        // too slow:
        // d1_k100_n10000, 1, 100, 10000;

        d2_k003_n00010, 2,   3,    10;
        d2_k003_n00100, 2,   3,   100;
        d2_k003_n01000, 2,   3,  1000;
        d2_k003_n10000, 2,   3, 10000;

        d2_k010_n00100, 2,  10,   100;
        d2_k010_n01000, 2,  10,  1000;
        d2_k010_n10000, 2,  10, 10000;

        d2_k100_n01000, 2, 100,  1000;
        // too slow:
        // d2_k100_n10000, 2, 100, 10000;

        d3_k003_n00010, 3,   3,    10;
        d3_k003_n00100, 3,   3,   100;
        d3_k003_n01000, 3,   3,  1000;
        d3_k003_n10000, 3,   3, 10000;

        d3_k010_n00100, 3,  10,   100;
        d3_k010_n01000, 3,  10,  1000;
        d3_k010_n10000, 3,  10, 10000;

        d3_k100_n01000, 3, 100,  1000;
        // too slow:
        // d3_k100_n10000, 3, 100, 10000;
        d9_k003_n00010, 9,   3,    10;
        d9_k003_n00100, 9,   3,   100;
        d9_k003_n01000, 9,   3,  1000;
        d9_k003_n10000, 9,   3, 10000;

        d9_k010_n00100, 9,  10,   100;
        d9_k010_n01000, 9,  10,  1000;
        d9_k010_n10000, 9,  10, 10000;

        d9_k100_n01000, 9, 100,  1000;
        // too slow:
        // d9_k100_n10000, 9, 100, 10000;
    }
}
<|endoftext|>


<commit_before>use super::{Array1dView, Array1dViewMut, Array2dView, Array2dViewMut};

use openblas::ffi::*;

#[derive(Clone, Copy)]
pub enum Transpose {
  N,
  T,
}

impl<'a> Array1dView<'a, f32> {
  pub fn l2_norm(&'a self) -> f32 {
    let n = self.dim();
    let incx = self.stride();
    unsafe { cblas_snrm2(
        n as _,
        self.buf.as_ptr(),
        incx as _,
    ) }
  }

  pub fn elem_sum(&'a self) -> f32 {
    let x_n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    let mut x_sum = 0.0;
    for _ in 0 .. x_n {
      let x_i = self.buf[p];
      x_sum += x_i;
      p += incx;
    }
    x_sum
  }

  pub fn inner_prod(&'a self, alpha: f32, y: Array1dView<'a, f32>) -> f32 {
    let x_n = self.dim();
    let y_n = y.dim();
    assert_eq!(x_n, y_n);
    let incx = self.stride();
    let incy = y.stride();
    unsafe { cblas_sdot(
        x_n as _,
        alpha,
        self.buf.as_ptr(),
        incx as _,
        y.as_ptr(),
        incy as _,
    ) }
  }
}

impl<'a> Array1dViewMut<'a, f32> {
  pub fn vector_add_scalar(&'a mut self, c: f32) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      self.buf[p] += c;
      p += incx;
    }
  }

  pub fn vector_square(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = x_i * x_i;
      p += incx;
    }
  }

  pub fn vector_sqrt(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = x_i.sqrt();
      p += incx;
    }
  }

  pub fn vector_recip(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = 1.0 / x_i;
      p += incx;
    }
  }

  pub fn vector_scale(&'a mut self, alpha: f32) {
    let n = self.dim();
    let incx = self.stride();
    unsafe { cblas_sscal(
        n as _,
        alpha,
        self.buf.as_mut_ptr(),
        incx as _,
    ) }
  }

  pub fn vector_add(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    unsafe { cblas_saxpy(
        x_n as _,
        alpha,
        x.buf.as_ptr(),
        incx as _,
        self.buf.as_mut_ptr(),
        incy as _,
    ) };
  }

  pub fn vector_elem_mult(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = alpha * x_i * y_i;
      p += incx;
      q += incy;
    }
  }

  pub fn elem_div(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = alpha * y_i / x_i;
      p += incx;
      q += incy;
    }
  }
}

impl<'a> Array2dView<'a, f32> {
  pub fn matrix_diag(&'a self, y: Array1dViewMut<'a, f32>) {
    let (a_m, a_n) = self.dim();
    let y_m = y.dim();
    assert_eq!(a_m, a_n);
    assert_eq!(a_m, y_m);
    let (a_inc, lda) = self.stride();
    let incy = y.stride();
    assert_eq!(1, a_inc);
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. y_m {
      y.buf[q] = self.buf[p];
      p += a_inc + lda;
      q += incy;
    }
  }
}

impl<'a> Array2dViewMut<'a, f32> {
  pub fn matrix_add(&'a mut self, alpha: f32, x: Array2dView<'a, f32>) {
    let (x_m, x_n) = x.dim();
    let (y_m, y_n) = self.dim();
    assert_eq!(x_m, y_m);
    assert_eq!(x_n, y_n);
    let (incx, ldx) = x.stride();
    let (incy, ldy) = self.stride();
    if x_n == 1 {
      unsafe { cblas_saxpy(
          x_m as _,
          alpha,
          x.buf.as_ptr(),
          incx as _,
          self.buf.as_mut_ptr(),
          incy as _,
      ) };
    } else if x_m == 1 {
      unimplemented!();
    } else {
      unimplemented!();
    }
  }

  pub fn matrix_prod(&'a mut self, alpha: f32, a: Array2dView<'a, f32>, a_trans: Transpose, b: Array2dView<'a, f32>, b_trans: Transpose, beta: f32) {
    let (a_m, a_n) = a.dim();
    let (b_m, b_n) = b.dim();
    let (c_m, c_n) = self.dim();
    let (at_m, at_n) = match a_trans {
      Transpose::N => (a_m, a_n),
      Transpose::T => (a_n, a_m),
    };
    let (bt_m, bt_n) = match b_trans {
      Transpose::N => (b_m, b_n),
      Transpose::T => (b_n, b_m),
    };
    assert_eq!(c_m, at_m);
    assert_eq!(c_n, bt_n);
    assert_eq!(at_n, bt_m);
    let k = at_n;
    let (a_inc, lda) = a.stride();
    let (b_inc, ldb) = b.stride();
    let (c_inc, ldc) = self.stride();
    assert_eq!(1, a_inc);
    assert_eq!(1, b_inc);
    assert_eq!(1, c_inc);
    unsafe { cblas_sgemm(
        CblasOrder::ColMajor,
        match a_trans {
          Transpose::N => CblasTranspose::NoTrans,
          Transpose::T => CblasTranspose::Trans,
        },
        match b_trans {
          Transpose::N => CblasTranspose::NoTrans,
          Transpose::T => CblasTranspose::Trans,
        },
        c_m as _, c_n as _, k as _,
        alpha,
        a.buf.as_ptr(), lda as _,
        b.buf.as_ptr(), ldb as _,
        beta,
        self.buf.as_mut_ptr(), ldc as _,
    ) };
  }
}

impl<'a> Array2dViewMut<'a, f64> {
  pub fn matrix_prod(&'a mut self) {
    unimplemented!();
  }
}
<commit_msg>Specify the sequential version of OpenBLAS by default. Implement a stable online averaging routine.<commit_after>use super::{Array1dView, Array1dViewMut, Array2dView, Array2dViewMut};

use openblas::ffi::*;

#[derive(Clone, Copy)]
pub enum Transpose {
  N,
  T,
}

impl<'a> Array1dView<'a, f32> {
  pub fn l2_norm(&'a self) -> f32 {
    let n = self.dim();
    let incx = self.stride();
    unsafe { openblas_sequential_cblas_snrm2(
        n as _,
        self.buf.as_ptr(),
        incx as _,
    ) }
  }

  pub fn elem_sum(&'a self) -> f32 {
    let x_n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    let mut x_sum = 0.0;
    for _ in 0 .. x_n {
      let x_i = self.buf[p];
      x_sum += x_i;
      p += incx;
    }
    x_sum
  }

  pub fn inner_prod(&'a self, alpha: f32, y: Array1dView<'a, f32>) -> f32 {
    let x_n = self.dim();
    let y_n = y.dim();
    assert_eq!(x_n, y_n);
    let incx = self.stride();
    let incy = y.stride();
    unsafe { openblas_sequential_cblas_sdot(
        x_n as _,
        alpha,
        self.buf.as_ptr(),
        incx as _,
        y.as_ptr(),
        incy as _,
    ) }
  }
}

impl<'a> Array1dViewMut<'a, f32> {
  pub fn vector_add_scalar(&'a mut self, c: f32) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      self.buf[p] += c;
      p += incx;
    }
  }

  pub fn vector_square(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = x_i * x_i;
      p += incx;
    }
  }

  pub fn vector_sqrt(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = x_i.sqrt();
      p += incx;
    }
  }

  pub fn vector_recip(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = 1.0 / x_i;
      p += incx;
    }
  }

  pub fn vector_scale(&'a mut self, alpha: f32) {
    let n = self.dim();
    let incx = self.stride();
    unsafe { openblas_sequential_cblas_sscal(
        n as _,
        alpha,
        self.buf.as_mut_ptr(),
        incx as _,
    ) }
  }

  pub fn vector_add(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    unsafe { openblas_sequential_cblas_saxpy(
        x_n as _,
        alpha,
        x.buf.as_ptr(),
        incx as _,
        self.buf.as_mut_ptr(),
        incy as _,
    ) };
  }

  pub fn average(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = alpha * (x_i - y_i);
      p += incx;
      q += incy;
    }
  }

  pub fn vector_elem_mult(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = alpha * x_i * y_i;
      p += incx;
      q += incy;
    }
  }

  pub fn elem_div(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = alpha * y_i / x_i;
      p += incx;
      q += incy;
    }
  }
}

impl<'a> Array2dView<'a, f32> {
  pub fn matrix_diag(&'a self, y: Array1dViewMut<'a, f32>) {
    let (a_m, a_n) = self.dim();
    let y_m = y.dim();
    assert_eq!(a_m, a_n);
    assert_eq!(a_m, y_m);
    let (a_inc, lda) = self.stride();
    let incy = y.stride();
    assert_eq!(1, a_inc);
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. y_m {
      y.buf[q] = self.buf[p];
      p += a_inc + lda;
      q += incy;
    }
  }
}

impl<'a> Array2dViewMut<'a, f32> {
  pub fn matrix_add(&'a mut self, alpha: f32, x: Array2dView<'a, f32>) {
    let (x_m, x_n) = x.dim();
    let (y_m, y_n) = self.dim();
    assert_eq!(x_m, y_m);
    assert_eq!(x_n, y_n);
    let (incx, ldx) = x.stride();
    let (incy, ldy) = self.stride();
    if x_n == 1 {
      unsafe { openblas_sequential_cblas_saxpy(
          x_m as _,
          alpha,
          x.buf.as_ptr(),
          incx as _,
          self.buf.as_mut_ptr(),
          incy as _,
      ) };
    } else if x_m == 1 {
      unimplemented!();
    } else {
      unimplemented!();
    }
  }

  pub fn matrix_prod(&'a mut self, alpha: f32, a: Array2dView<'a, f32>, a_trans: Transpose, b: Array2dView<'a, f32>, b_trans: Transpose, beta: f32) {
    let (a_m, a_n) = a.dim();
    let (b_m, b_n) = b.dim();
    let (c_m, c_n) = self.dim();
    let (at_m, at_n) = match a_trans {
      Transpose::N => (a_m, a_n),
      Transpose::T => (a_n, a_m),
    };
    let (bt_m, bt_n) = match b_trans {
      Transpose::N => (b_m, b_n),
      Transpose::T => (b_n, b_m),
    };
    assert_eq!(c_m, at_m);
    assert_eq!(c_n, bt_n);
    assert_eq!(at_n, bt_m);
    let k = at_n;
    let (a_inc, lda) = a.stride();
    let (b_inc, ldb) = b.stride();
    let (c_inc, ldc) = self.stride();
    assert_eq!(1, a_inc);
    assert_eq!(1, b_inc);
    assert_eq!(1, c_inc);
    unsafe { openblas_sequential_cblas_sgemm(
        CblasOrder::ColMajor,
        match a_trans {
          Transpose::N => CblasTranspose::NoTrans,
          Transpose::T => CblasTranspose::Trans,
        },
        match b_trans {
          Transpose::N => CblasTranspose::NoTrans,
          Transpose::T => CblasTranspose::Trans,
        },
        c_m as _, c_n as _, k as _,
        alpha,
        a.buf.as_ptr(), lda as _,
        b.buf.as_ptr(), ldb as _,
        beta,
        self.buf.as_mut_ptr(), ldc as _,
    ) };
  }
}

impl<'a> Array2dViewMut<'a, f64> {
  pub fn matrix_prod(&'a mut self) {
    unimplemented!();
  }
}
<|endoftext|>


<commit_before>// Used to specify checks that shouldn't fail (but might in unsafe)
macro_rules! dbg_gl_error {
    ($($pat:pat => $msg:expr),*) => {
        if cfg!(debug_assertions) {
            let err = $crate::Context::get_error();
            match err {
                $(Some($pat) => {
                    panic!("OpenGL error {:?} - {}", err, $msg)
                }),*
                None => { }
            }
        }
    }
}

// Used to specify checks that should *never* be able to fail (even in unsafe!)
macro_rules! dbg_gl_sanity_check {
    ($($pat:pat => $msg:expr),*) => {
        dbg_gl_error! { $($pat => concat!("Sanity check failed: ", $msg)),* }
    }
}

macro_rules! gl_enum {
    (
        pub gl_enum $name:ident {
            $($variant:ident as $const_name:ident = $value:expr),+
        }
    ) => {
        #[derive(Debug, Clone, Copy)]
        pub enum $name {
            $($variant = $value as isize),+
        }
        $(pub const $const_name: $name = $name::$variant;)+

        #[allow(dead_code)]
        impl $name {
            pub fn from_gl(gl_enum: $crate::gl::types::GLenum)
                -> Result<Self, ()>
            {
                match gl_enum {
                    $(x if x == $value => { Ok($name::$variant) },)+
                    _ => { Err(()) }
                }
            }

            pub fn gl_enum(&self) -> $crate::gl::types::GLenum {
                *self as $crate::gl::types::GLenum
            }
        }
    }
}
<commit_msg>Derive `Eq` for all GL enums (crated from macros)<commit_after>// Used to specify checks that shouldn't fail (but might in unsafe)
macro_rules! dbg_gl_error {
    ($($pat:pat => $msg:expr),*) => {
        if cfg!(debug_assertions) {
            let err = $crate::Context::get_error();
            match err {
                $(Some($pat) => {
                    panic!("OpenGL error {:?} - {}", err, $msg)
                }),*
                None => { }
            }
        }
    }
}

// Used to specify checks that should *never* be able to fail (even in unsafe!)
macro_rules! dbg_gl_sanity_check {
    ($($pat:pat => $msg:expr),*) => {
        dbg_gl_error! { $($pat => concat!("Sanity check failed: ", $msg)),* }
    }
}

macro_rules! gl_enum {
    (
        pub gl_enum $name:ident {
            $($variant:ident as $const_name:ident = $value:expr),+
        }
    ) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        pub enum $name {
            $($variant = $value as isize),+
        }
        $(pub const $const_name: $name = $name::$variant;)+

        #[allow(dead_code)]
        impl $name {
            pub fn from_gl(gl_enum: $crate::gl::types::GLenum)
                -> Result<Self, ()>
            {
                match gl_enum {
                    $(x if x == $value => { Ok($name::$variant) },)+
                    _ => { Err(()) }
                }
            }

            pub fn gl_enum(&self) -> $crate::gl::types::GLenum {
                *self as $crate::gl::types::GLenum
            }
        }
    }
}
<|endoftext|>