<commit_before>#![crate_name="initdb"]
#![crate_type = "bin"]

#![feature(core, old_io)]

extern crate sqlite3;

mod init {
    use sqlite3::{open, Database, SqliteResult};

    pub fn write_db(db : &mut Database) -> SqliteResult<()> {
        try!(db.exec("CREATE TABLE Words(Word TEXT);"));
        try!(db.exec("CREATE TABLE Definitions(Definee TEXT, Idx INTEGER, Definer TEXT);"));
        try!(db.exec("CREATE TABLE Log(Word TEXT, Timestamp INTEGER);"));

        let mut input = ::std::old_io::stdin();
        for line in input.lock().lines() {
            let word = line.unwrap().clone();
            let trimmed = word.as_slice().trim();
            assert!(trimmed.chars().all(|c| c.is_alphanumeric()), "not alphanumeric: {}", trimmed);
            try!(db.exec(format!("INSERT INTO Words VALUES(\"{}\");", trimmed).as_slice()));
        }
        Ok(())
    }

    pub fn open_db() -> SqliteResult<Database> {
        let args : Vec<String> = ::std::env::args().collect();
        return open(args[1].as_slice());
    }

    pub fn main() {
        match open_db() {
            Ok(mut db) => {
               match write_db(&mut db) {
                   Ok(()) => {}
                   Err(e) => { println!("error: {:?}, ({})", e, db.get_errmsg()) }
               }
            }
            Err(e) => {
               println!("could not open database: {:?}", e);
            }
        }
    }
}

pub fn main() {
    init::main();
}
<commit_msg>new io in initdb<commit_after>#![crate_name="initdb"]
#![crate_type = "bin"]

#![feature(core, io)]

extern crate sqlite3;

mod init {
    use sqlite3::{open, Database, SqliteResult};
    use std::io::BufReadExt;

    pub fn write_db(db : &mut Database) -> SqliteResult<()> {
        try!(db.exec("CREATE TABLE Words(Word TEXT);"));
        try!(db.exec("CREATE TABLE Definitions(Definee TEXT, Idx INTEGER, Definer TEXT);"));
        try!(db.exec("CREATE TABLE Log(Word TEXT, Timestamp INTEGER);"));

        let input = ::std::io::stdin();
        for line in input.lock().lines() {
            let word = line.unwrap().clone();
            let trimmed = word.as_slice().trim();
            assert!(trimmed.chars().all(|c| c.is_alphanumeric()), "not alphanumeric: {}", trimmed);
            try!(db.exec(format!("INSERT INTO Words VALUES(\"{}\");", trimmed).as_slice()));
        }
        Ok(())
    }

    pub fn open_db() -> SqliteResult<Database> {
        let args : Vec<String> = ::std::env::args().collect();
        return open(args[1].as_slice());
    }

    pub fn main() {
        match open_db() {
            Ok(mut db) => {
               match write_db(&mut db) {
                   Ok(()) => {}
                   Err(e) => { println!("error: {:?}, ({})", e, db.get_errmsg()) }
               }
            }
            Err(e) => {
               println!("could not open database: {:?}", e);
            }
        }
    }
}

pub fn main() {
    init::main();
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate assert_cli;
#[macro_use]
extern crate lazy_static;

use std::env;
use std::str;
use std::path::{Path, PathBuf};

static EMPTY: &'static [&'static str] = &[];
lazy_static! {
    static ref _CWD: PathBuf = env::current_dir().unwrap();
    static ref CWD: &'static Path = _CWD.as_path();
    // TODO test on release
    static ref _BIN: PathBuf = CWD.join("target/debug/cobalt");
    static ref BIN: &'static str = _BIN.to_str().unwrap();
}

macro_rules! assert_contains {
    ($utf8: expr, $vec: expr) => {
        let text = str::from_utf8($utf8).unwrap();
        println!("{}", text);
        assert!(text.contains($vec))
    }
}

macro_rules! assert_contains_not {
    ($utf8: expr, $vec: expr) => {
        let text = str::from_utf8($utf8).unwrap();
        println!("{}", text);
        assert!(!text.contains($vec))
    }
}

#[test]
pub fn invalid_calls() {
    println!("Binary: {:?}", BIN.to_owned());
    let output = assert_cli!(&BIN, EMPTY => Error 1).unwrap();
    assert_contains!(&output.stderr, "requires a subcommand");

    let output = assert_cli!(&BIN, &["--nonexistent-argument"] => Error 1).unwrap();
    assert_contains!(&output.stderr,
                     r"Found argument '--nonexistent-argument' which wasn't expected");
}

#[test]
pub fn log_levels() {
    env::set_current_dir(CWD.join("tests/fixtures/example")).unwrap();

    let output1 = assert_cli!(&BIN, &["build", "--trace"] => Success).unwrap();
    assert_contains!(&output1.stderr, "[trace]");
    assert_contains!(&output1.stderr, "[debug]");
    assert_contains!(&output1.stderr, "[info]");

    let output2 = assert_cli!(&BIN, &["build", "-L", "trace"] => Success).unwrap();
    assert_eq!(output1.stderr, output2.stderr);

    let output = assert_cli!(&BIN, &["build", "-L", "debug"] => Success).unwrap();
    assert_contains_not!(&output.stderr, "[trace]");
    assert_contains!(&output.stderr, "[debug]");
    assert_contains!(&output.stderr, "[info]");

    let output = assert_cli!(&BIN, &["build", "-L", "info"] => Success).unwrap();
    assert_contains_not!(&output.stderr, "[trace]");
    assert_contains_not!(&output.stderr, "[debug]");
    assert_contains!(&output.stderr, "[info]");

    assert_cli!(&BIN, &["build", "--silent"] => Success, "").unwrap();
}

#[test]
pub fn clean() {
    env::set_current_dir(CWD.join("tests/fixtures/example")).unwrap();
    assert_cli!(&BIN, &["build", "-d", "./test_dest"] => Success).unwrap();
    assert_eq!(Path::new("./test_dest/").is_dir(), true);

    let output = assert_cli!(&BIN, &["clean", "-d", "./test_dest"] => Success).unwrap();
    assert_eq!(Path::new("./test_dest").is_dir(), false);
    assert_contains!(&output.stderr, "directory \"./test_dest\" removed");
}

#[cfg(not(windows))]
#[test]
pub fn clean_warning() {
    env::set_current_dir(CWD.join("tests/fixtures/example")).unwrap();
    let output = assert_cli!(&BIN, &["clean"] => Error 1).unwrap();
    assert_contains!(&output.stderr,
                     "Destination directory is same as current directory. Cancelling the \
                      operation");
}
<commit_msg>fix(tests/cli/log_levels): Improve comparison error reporting<commit_after>#[macro_use]
extern crate difference;
#[macro_use]
extern crate assert_cli;
#[macro_use]
extern crate lazy_static;

use std::env;
use std::str;
use std::path::{Path, PathBuf};

static EMPTY: &'static [&'static str] = &[];
lazy_static! {
    static ref _CWD: PathBuf = env::current_dir().unwrap();
    static ref CWD: &'static Path = _CWD.as_path();
    // TODO test on release
    static ref _BIN: PathBuf = CWD.join("target/debug/cobalt");
    static ref BIN: &'static str = _BIN.to_str().unwrap();
}

macro_rules! assert_contains {
    ($utf8: expr, $vec: expr) => {
        let text = str::from_utf8($utf8).unwrap();
        println!("{}", text);
        assert!(text.contains($vec))
    }
}

macro_rules! assert_contains_not {
    ($utf8: expr, $vec: expr) => {
        let text = str::from_utf8($utf8).unwrap();
        println!("{}", text);
        assert!(!text.contains($vec))
    }
}

#[test]
pub fn invalid_calls() {
    println!("Binary: {:?}", BIN.to_owned());
    let output = assert_cli!(&BIN, EMPTY => Error 1).unwrap();
    assert_contains!(&output.stderr, "requires a subcommand");

    let output = assert_cli!(&BIN, &["--nonexistent-argument"] => Error 1).unwrap();
    assert_contains!(&output.stderr,
                     r"Found argument '--nonexistent-argument' which wasn't expected");
}

#[test]
pub fn log_levels() {
    env::set_current_dir(CWD.join("tests/fixtures/example")).unwrap();

    let output1 = assert_cli!(&BIN, &["build", "--trace"] => Success).unwrap();
    let output1_stderr = str::from_utf8(&output1.stderr).to_owned().unwrap();
    assert_contains!(&output1.stderr, "[trace]");
    assert_contains!(&output1.stderr, "[debug]");
    assert_contains!(&output1.stderr, "[info]");

    let output2 = assert_cli!(&BIN, &["build", "-L", "trace"] => Success).unwrap();
    let output2_stderr = str::from_utf8(&output2.stderr).to_owned().unwrap();
    assert_diff!(&output1_stderr, &output2_stderr, " ", 0);

    let output = assert_cli!(&BIN, &["build", "-L", "debug"] => Success).unwrap();
    assert_contains_not!(&output.stderr, "[trace]");
    assert_contains!(&output.stderr, "[debug]");
    assert_contains!(&output.stderr, "[info]");

    let output = assert_cli!(&BIN, &["build", "-L", "info"] => Success).unwrap();
    assert_contains_not!(&output.stderr, "[trace]");
    assert_contains_not!(&output.stderr, "[debug]");
    assert_contains!(&output.stderr, "[info]");

    assert_cli!(&BIN, &["build", "--silent"] => Success, "").unwrap();
}

#[test]
pub fn clean() {
    env::set_current_dir(CWD.join("tests/fixtures/example")).unwrap();
    assert_cli!(&BIN, &["build", "-d", "./test_dest"] => Success).unwrap();
    assert_eq!(Path::new("./test_dest/").is_dir(), true);

    let output = assert_cli!(&BIN, &["clean", "-d", "./test_dest"] => Success).unwrap();
    assert_eq!(Path::new("./test_dest").is_dir(), false);
    assert_contains!(&output.stderr, "directory \"./test_dest\" removed");
}

#[cfg(not(windows))]
#[test]
pub fn clean_warning() {
    env::set_current_dir(CWD.join("tests/fixtures/example")).unwrap();
    let output = assert_cli!(&BIN, &["clean"] => Error 1).unwrap();
    assert_contains!(&output.stderr,
                     "Destination directory is same as current directory. Cancelling the \
                      operation");
}
<|endoftext|>


<commit_before>extern crate staticdir;
extern crate iron;
extern crate hyper;
extern crate mount;

use iron::prelude::*;

use mount::Mount;

use hyper::Client;
use hyper::status;
use hyper::header::{ ContentType };
use hyper::mime::{ Mime, TopLevel, SubLevel };

use std::io::Read;
use staticdir::{ StaticDir, AsJson };

use std::ops::Deref;

#[test]
fn handler_provides_json() {
    let mut server = Iron::new(StaticDir::new("tests/mount", AsJson)).http("localhost:3000").unwrap();

    let client = Client::new();
    let mut res = client.get("http://localhost:3000").send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    server.close().unwrap();

    assert_eq!(res.status, status::StatusCode::Ok);
    let &Mime(ref top, ref sub, _) = res.headers.get::<ContentType>().unwrap().deref();
    assert_eq!((top, sub), (&TopLevel::Application, &SubLevel::Json));
    assert_eq!(body,  "[{\"is_file\":true,\"is_dir\":false,\"is_symlink\":false,\"path\":\"tests/mount/1.txt\",\"file_name\":\"1.txt\"},{\"is_file\":false,\"is_dir\":true,\"is_symlink\":false,\"path\":\"tests/mount/nested\",\"file_name\":\"nested\"}]");
}

#[test]
fn should_work_with_mount() {
    let mut mount = Mount::new();
    mount.mount("/mnt/", StaticDir::new("tests/mount", AsJson));
    let mut server = Iron::new(mount).http("localhost:3000").unwrap();

    let client = Client::new();
    let mut res = client.get("http://localhost:3000/mnt").send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    server.close().unwrap();

    assert_eq!(res.status, status::StatusCode::Ok);
    let &Mime(ref top, ref sub, _) = res.headers.get::<ContentType>().unwrap().deref();
    assert_eq!((top, sub), (&TopLevel::Application, &SubLevel::Json));
    assert_eq!(body,  "[{\"is_file\":true,\"is_dir\":false,\"is_symlink\":false,\"path\":\"tests/mount/1.txt\",\"file_name\":\"1.txt\"},{\"is_file\":false,\"is_dir\":true,\"is_symlink\":false,\"path\":\"tests/mount/nested\",\"file_name\":\"nested\"}]");
}
<commit_msg>fixed failing test<commit_after>extern crate staticdir;
extern crate iron;
extern crate hyper;
extern crate mount;

use iron::prelude::*;

use mount::Mount;

use hyper::Client;
use hyper::status;
use hyper::header::{ ContentType };
use hyper::mime::{ Mime, TopLevel, SubLevel };

use std::io::Read;
use staticdir::{ StaticDir, AsJson };

use std::ops::Deref;

#[test]
fn handler_provides_json() {
    let mut server = Iron::new(StaticDir::new("tests/mount", AsJson)).http("localhost:3000").unwrap();

    let client = Client::new();
    let mut res = client.get("http://localhost:3000").send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    server.close().unwrap();

    assert_eq!(res.status, status::StatusCode::Ok);
    let &Mime(ref top, ref sub, _) = res.headers.get::<ContentType>().unwrap().deref();
    assert_eq!((top, sub), (&TopLevel::Application, &SubLevel::Json));
    assert_eq!(body,  "[{\"is_file\":true,\"is_dir\":false,\"is_symlink\":false,\"path\":\"tests/mount/1.txt\",\"file_name\":\"1.txt\"},{\"is_file\":false,\"is_dir\":true,\"is_symlink\":false,\"path\":\"tests/mount/nested\",\"file_name\":\"nested\"}]");
}

#[test]
fn should_work_with_mount() {
    let mut mount = Mount::new();
    mount.mount("/mnt/", StaticDir::new("tests/mount", AsJson));
    let mut server = Iron::new(mount).http("localhost:3001").unwrap();

    let client = Client::new();
    let mut res = client.get("http://localhost:3001/mnt").send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    server.close().unwrap();

    assert_eq!(res.status, status::StatusCode::Ok);
    let &Mime(ref top, ref sub, _) = res.headers.get::<ContentType>().unwrap().deref();
    assert_eq!((top, sub), (&TopLevel::Application, &SubLevel::Json));
    assert_eq!(body,  "[{\"is_file\":true,\"is_dir\":false,\"is_symlink\":false,\"path\":\"tests/mount/1.txt\",\"file_name\":\"1.txt\"},{\"is_file\":false,\"is_dir\":true,\"is_symlink\":false,\"path\":\"tests/mount/nested\",\"file_name\":\"nested\"}]");
}
<|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! benches {
        ($($name: ident, $k: expr, $n: expr;)*) => {
            $(
                #[bench]
                fn $name(b: &mut Bencher) {
                    let k = $k;
                    let n = $n;

                    let mut rng = XorShiftRng::new_unseeded();
                    let points = rng
                        .gen_iter::<f64>()
                        .take(n)
                        .map(|f| Euclid([f]))
                        .collect::<Vec<_>>();

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

    benches! {
        k003_n00010,   3,    10;
        k003_n00100,   3,   100;
        k003_n01000,   3,  1000;
        k003_n10000,   3, 10000;

        k010_n00100,  10,   100;
        k010_n01000,  10,  1000;
        k010_n10000,  10, 10000;

        k100_n01000, 100,  1000;
        // too slow:
        // k100_n10000, 100, 10000;
    }
}
<commit_msg>kmeans: add higher-dimensional 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>()]
        };
    }
    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;
    }
}
<|endoftext|>


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

pub struct Translator
{
	rules: HashMap<(char, char), char>,
}

impl Translator
{
	pub fn new() -> Translator
	{
		Translator
		{
			rules: get_default_rules(),
		}
	}

	fn translate_char(&self, current : char, previous : char) -> char
	{
		match self.rules.get(&(current, previous))
		{
			Some(result) => *result,
			None => match self.rules.get(&(current, ' '))
			{
				Some(result) => *result,
				None => current,
			}
		}
	}

	pub fn translate(&self, text : &str) -> String
	{
		let mut result = String::with_capacity(text.len());

		let mut previous = ' ';
		for c in text.chars()
		{
			previous = self.translate_char(c, previous);
			result.push(previous);
		}

		result
	}
}

fn get_default_rules() -> HashMap<(char, char), char>
{
	let mut result = HashMap::with_capacity(50);

	result.insert(('A', ' '), 'А');
	result.insert(('a', ' '), 'а');

	result
}
<commit_msg>Translation rules<commit_after>use std::collections::HashMap;

pub struct Translator
{
	rules: HashMap<(char, char), char>,
}

impl Translator
{
	pub fn new() -> Translator
	{
		Translator
		{
			rules: get_default_rules(),
		}
	}

	fn translate_char(&self, current : char, previous : char) -> char
	{
		match self.rules.get(&(current, previous))
		{
			Some(result) => *result,
			None => match self.rules.get(&(current, ' '))
			{
				Some(result) => *result,
				None => current,
			}
		}
	}

	pub fn translate(&self, text : &str) -> String
	{
		let mut result = String::with_capacity(text.len());

		let mut previous = ' ';
		for c in text.chars()
		{
			previous = self.translate_char(c, previous);
			result.push(previous);
		}

		result
	}
}

fn get_default_rules() -> HashMap<(char, char), char>
{
	let mut result = HashMap::with_capacity(50);

	result.insert(('A', ' '), 'А');
	result.insert(('a', ' '), 'а');
	result.insert(('B', ' '), 'Б');
	result.insert(('b', ' '), 'б');
	result.insert(('V', ' '), 'В');
	result.insert(('v', ' '), 'в');
	result.insert(('G', ' '), 'Г');
	result.insert(('g', ' '), 'г');
	result.insert(('D', ' '), 'Д');
	result.insert(('d', ' '), 'д');
	result.insert(('E', ' '), 'Е');
	result.insert(('e', ' '), 'е');
	result.insert(('O', 'J'), 'Ё');
	result.insert(('O', 'Й'), 'Ё');
	result.insert(('o', 'j'), 'ё');
	result.insert(('o', 'й'), 'ё');
	result.insert(('H', 'Z'), 'Ж');
	result.insert(('H', 'З'), 'Ж');
	result.insert(('h', 'z'), 'ж');
	result.insert(('h', 'з'), 'ж');
	result.insert(('Z', ' '), 'З');
	result.insert(('z', ' '), 'з');
	result.insert(('I', ' '), 'И');
	result.insert(('i', ' '), 'и');
	result.insert(('J', ' '), 'Й');
	result.insert(('j', ' '), 'й');
	result.insert(('K', ' '), 'К');
	result.insert(('k', ' '), 'к');
	result.insert(('L', ' '), 'Л');
	result.insert(('l', ' '), 'л');
	result.insert(('M', ' '), 'М');
	result.insert(('m', ' '), 'м');
	result.insert(('N', ' '), 'Н');
	result.insert(('n', ' '), 'н');
	result.insert(('O', ' '), 'О');
	result.insert(('o', ' '), 'о');
	result.insert(('P', ' '), 'П');
	result.insert(('p', ' '), 'п');
	result.insert(('R', ' '), 'Р');
	result.insert(('r', ' '), 'р');
	result.insert(('S', ' '), 'С');
	result.insert(('s', ' '), 'с');
	result.insert(('T', ' '), 'Т');
	result.insert(('t', ' '), 'т');
	result.insert(('U', ' '), 'У');
	result.insert(('u', ' '), 'у');
	result.insert(('F', ' '), 'Ф');
	result.insert(('f', ' '), 'ф');
	result.insert(('H', ' '), 'Х');
	result.insert(('X', ' '), 'Х');
	result.insert(('h', ' '), 'х');
	result.insert(('x', ' '), 'х');
	result.insert(('C', ' '), 'Ц');
	result.insert(('c', ' '), 'ц');
	result.insert(('H', 'C'), 'Ч');
	result.insert(('H', 'Ц'), 'Ч');
	result.insert(('h', 'c'), 'ч');
	result.insert(('h', 'ц'), 'ч');
	result.insert(('H', 'H'), 'Ш');
	result.insert(('H', 'С'), 'Ш');
	result.insert(('h', 'h'), 'ш');
	result.insert(('h', 'с'), 'ш');
	result.insert(('H', 'Ш'), 'Щ');
	result.insert(('W', ' '), 'Щ');
	result.insert(('h', 'ш'), 'щ');
	result.insert(('w', ' '), 'щ');
	result.insert(('#', 'ъ'), 'Ъ');
	result.insert(('#', ' '), 'ъ');
	result.insert(('Y', ' '), 'Ы');
	result.insert(('y', ' '), 'ы');
	result.insert(('\'', 'ь'), 'Ь');
	result.insert(('\'', ' '), 'ь');
	result.insert(('E', 'J'), 'Э');
	result.insert(('E', 'Й'), 'Э');
	result.insert(('e', 'j'), 'э');
	result.insert(('e', 'й'), 'э');
	result.insert(('U', 'J'), 'Ю');
	result.insert(('U', 'Й'), 'Ю');
	result.insert(('u', 'j'), 'ю');
	result.insert(('u', 'й'), 'ю');
	result.insert(('A', 'J'), 'Я');
	result.insert(('A', 'Й'), 'Я');
	result.insert(('a', 'j'), 'я');
	result.insert(('a', 'й'), 'я');

	result
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use color::Color;
use geometry::{DevicePixel, LayerPixel};
use tiling::{Tile, TileGrid};

use euclid::matrix::Matrix4;
use euclid::scale_factor::ScaleFactor;
use euclid::size::{Size2D, TypedSize2D};
use euclid::point::{Point2D, TypedPoint2D};
use euclid::rect::{Rect, TypedRect};
use platform::surface::{NativeDisplay, NativeSurface};
use std::cell::{RefCell, RefMut};
use std::rc::Rc;
use util::{project_rect_to_screen, ScreenRect};

#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct ContentAge {
    age: usize,
}

impl ContentAge {
    pub fn new() -> ContentAge {
        ContentAge {
            age: 0,
        }
    }

    pub fn next(&mut self) {
        self.age += 1;
    }
}

pub struct TransformState {
    /// Final, concatenated transform + perspective matrix for this layer
    pub final_transform: Matrix4,

    /// If this is none, the rect was clipped and is not visible at all!
    pub screen_rect: Option<ScreenRect>,

    /// Rectangle in global coordinates, but not transformed.
    pub world_rect: Rect<f32>,

    /// True if this layer has a 3D transform
    pub is_3d: bool,
}

impl TransformState {
    fn new() -> TransformState {
        TransformState {
            final_transform: Matrix4::identity(),
            screen_rect: None,
            world_rect: Rect::zero(),
            is_3d: false,
        }
    }
}

pub struct Layer<T> {
    pub children: RefCell<Vec<Rc<Layer<T>>>>,
    pub transform: RefCell<Matrix4>,
    pub perspective: RefCell<Matrix4>,
    pub tile_size: usize,
    pub extra_data: RefCell<T>,
    tile_grid: RefCell<TileGrid>,

    /// The boundaries of this layer in the coordinate system of the parent layer.
    pub bounds: RefCell<TypedRect<LayerPixel, f32>>,

    /// A monotonically increasing counter that keeps track of the current content age.
    pub content_age: RefCell<ContentAge>,

    /// The content offset for this layer in unscaled layer pixels.
    pub content_offset: RefCell<TypedPoint2D<LayerPixel, f32>>,

    /// Whether this layer clips its children to its boundaries.
    pub masks_to_bounds: RefCell<bool>,

    /// The background color for this layer.
    pub background_color: RefCell<Color>,

    /// The opacity of this layer, from 0.0 (fully transparent) to 1.0 (fully opaque).
    pub opacity: RefCell<f32>,

    /// Whether this stacking context creates a new 3d rendering context.
    pub establishes_3d_context: bool,

    /// Collection of state related to transforms for this layer.
    pub transform_state: RefCell<TransformState>,
}

impl<T> Layer<T> {
    pub fn new(bounds: TypedRect<LayerPixel, f32>,
               tile_size: usize,
               background_color: Color,
               opacity: f32,
               establishes_3d_context: bool,
               data: T)
               -> Layer<T> {
        Layer {
            children: RefCell::new(vec!()),
            transform: RefCell::new(Matrix4::identity()),
            perspective: RefCell::new(Matrix4::identity()),
            bounds: RefCell::new(bounds),
            tile_size: tile_size,
            extra_data: RefCell::new(data),
            tile_grid: RefCell::new(TileGrid::new(tile_size)),
            content_age: RefCell::new(ContentAge::new()),
            masks_to_bounds: RefCell::new(false),
            content_offset: RefCell::new(Point2D::zero()),
            background_color: RefCell::new(background_color),
            opacity: RefCell::new(opacity),
            establishes_3d_context: establishes_3d_context,
            transform_state: RefCell::new(TransformState::new()),
        }
    }

    pub fn children<'a>(&'a self) -> RefMut<'a,Vec<Rc<Layer<T>>>> {
        self.children.borrow_mut()
    }

    pub fn add_child(&self, new_child: Rc<Layer<T>>) {
        self.children().push(new_child);
    }

    pub fn remove_child_at_index(&self, index: usize) {
        self.children().remove(index);
    }

    /// Returns buffer requests inside the given dirty rect, and simultaneously throws out tiles
    /// outside the given viewport rect.
    pub fn get_buffer_requests(&self,
                               rect_in_layer: TypedRect<LayerPixel, f32>,
                               viewport_in_layer: TypedRect<LayerPixel, f32>,
                               scale: ScaleFactor<LayerPixel, DevicePixel, f32>)
                               -> Vec<BufferRequest> {
        let mut tile_grid = self.tile_grid.borrow_mut();
        tile_grid.get_buffer_requests_in_rect(rect_in_layer * scale,
                                              viewport_in_layer * scale,
                                              self.bounds.borrow().size * scale,
                                              &(self.transform_state.borrow().world_rect.origin *
                                                scale.get()),
                                              &self.transform_state.borrow().final_transform,
                                              *self.content_age.borrow())
    }

    pub fn resize(&self, new_size: TypedSize2D<LayerPixel, f32>) {
        self.bounds.borrow_mut().size = new_size;
    }

    pub fn add_buffer(&self, tile: Box<LayerBuffer>) {
        self.tile_grid.borrow_mut().add_buffer(tile);
    }

    pub fn collect_unused_buffers(&self) -> Vec<Box<LayerBuffer>> {
        self.tile_grid.borrow_mut().take_unused_buffers()
    }

    pub fn collect_buffers(&self) -> Vec<Box<LayerBuffer>> {
        self.tile_grid.borrow_mut().collect_buffers()
    }

    pub fn contents_changed(&self) {
        self.content_age.borrow_mut().next();
    }

    pub fn create_textures(&self, display: &NativeDisplay) {
        self.tile_grid.borrow_mut().create_textures(display);
    }

    pub fn do_for_all_tiles<F: FnMut(&Tile)>(&self, f: F) {
        self.tile_grid.borrow().do_for_all_tiles(f);
    }

    pub fn update_transform_state(&self,
                                  parent_transform: &Matrix4,
                                  parent_perspective: &Matrix4,
                                  parent_origin: &Point2D<f32>) {
        let mut ts = self.transform_state.borrow_mut();
        let rect_without_scroll = self.bounds.borrow()
                                             .to_untyped()
                                             .translate(parent_origin);

        ts.world_rect = rect_without_scroll.translate(&self.content_offset.borrow().to_untyped());

        let x0 = ts.world_rect.origin.x;
        let y0 = ts.world_rect.origin.y;

        // Build world space transform
        let local_transform = Matrix4::identity().translate(x0, y0, 0.0)
                                                 .mul(&*self.transform.borrow())
                                                 .translate(-x0, -y0, 0.0);

        ts.final_transform = parent_perspective.mul(&local_transform).mul(&parent_transform);
        ts.screen_rect = project_rect_to_screen(&ts.world_rect, &ts.final_transform);

        // TODO(gw): This is quite bogus. It's a hack to allow the paint task
        // to avoid "optimizing" 3d layers with an incorrect clip rect.
        // We should probably make the display list optimizer work with transforms!
        // This layer is part of a 3d context if its concatenated transform
        // is not identity, since 2d transforms don't get layers.
        ts.is_3d = ts.final_transform != Matrix4::identity();

        // Build world space perspective transform
        let perspective_transform = Matrix4::identity().translate(x0, y0, 0.0)
                                                       .mul(&*self.perspective.borrow())
                                                       .translate(-x0, -y0, 0.0);

        for child in self.children().iter() {
            child.update_transform_state(&ts.final_transform,
                                         &perspective_transform,
                                         &rect_without_scroll.origin);
        }
    }

    /// Calculate the amount of memory used by this layer and all its children.
    /// The memory may be allocated on the heap or in GPU memory.
    pub fn get_memory_usage(&self) -> usize {
        let size_of_children : usize = self.children().iter().map(|ref child| -> usize {
            child.get_memory_usage()
        }).sum();
        size_of_children + self.tile_grid.borrow().get_memory_usage()
    }
}

/// A request from the compositor to the renderer for tiles that need to be (re)displayed.
pub struct BufferRequest {
    /// The rect in pixels that will be drawn to the screen
    pub screen_rect: Rect<usize>,

    /// The rect in page coordinates that this tile represents
    pub page_rect: Rect<f32>,

    /// The content age of that this BufferRequest corresponds to.
    pub content_age: ContentAge,

    /// A cached LayerBuffer that can be used to avoid allocating a new one.
    pub layer_buffer: Option<Box<LayerBuffer>>,
}

impl BufferRequest {
    pub fn new(screen_rect: Rect<usize>, page_rect: Rect<f32>, content_age: ContentAge)
               -> BufferRequest {
        BufferRequest {
            screen_rect: screen_rect,
            page_rect: page_rect,
            content_age: content_age,
            layer_buffer: None,
        }
    }
}

pub struct LayerBuffer {
    /// The native surface which can be shared between threads or processes. On Mac this is an
    /// `IOSurface`; on Linux this is an X Pixmap; on Android this is an `EGLImageKHR`.
    pub native_surface: NativeSurface,

    /// The rect in the containing RenderLayer that this represents.
    pub rect: Rect<f32>,

    /// The rect in pixels that will be drawn to the screen.
    pub screen_pos: Rect<usize>,

    /// The scale at which this tile is rendered
    pub resolution: f32,

    /// Whether or not this buffer was painted with the CPU rasterization.
    pub painted_with_cpu: bool,

    /// The content age of that this buffer request corresponds to.
    pub content_age: ContentAge,
}

impl LayerBuffer {
    /// Returns the amount of memory used by the tile
    pub fn get_mem(&self) -> usize {
        self.native_surface.get_memory_usage()
    }

    /// Returns true if the tile is displayable at the given scale
    pub fn is_valid(&self, scale: f32) -> bool {
        (self.resolution - scale).abs() < 1.0e-6
    }

    /// Returns the Size2D of the tile
    pub fn get_size_2d(&self) -> Size2D<usize> {
        self.screen_pos.size
    }

    /// Marks the layer buffer as not leaking. See comments on
    /// `NativeSurfaceMethods::mark_wont_leak` for how this is used.
    pub fn mark_wont_leak(&mut self) {
        self.native_surface.mark_wont_leak()
    }

    /// Destroys the layer buffer. Painting task only.
    pub fn destroy(self, display: &NativeDisplay) {
        let mut this = self;
        this.native_surface.destroy(display)
    }
}

/// A set of layer buffers. This is an atomic unit used to switch between the front and back
/// buffers.
pub struct LayerBufferSet {
    pub buffers: Vec<Box<LayerBuffer>>
}

impl LayerBufferSet {
    /// Notes all buffer surfaces will leak if not destroyed via a call to `destroy`.
    pub fn mark_will_leak(&mut self) {
        for buffer in self.buffers.iter_mut() {
            buffer.native_surface.mark_will_leak()
        }
    }
}
<commit_msg>Send NativeSurfaces with BufferRequests<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use color::Color;
use geometry::{DevicePixel, LayerPixel};
use tiling::{Tile, TileGrid};

use euclid::matrix::Matrix4;
use euclid::scale_factor::ScaleFactor;
use euclid::size::{Size2D, TypedSize2D};
use euclid::point::{Point2D, TypedPoint2D};
use euclid::rect::{Rect, TypedRect};
use platform::surface::{NativeDisplay, NativeSurface};
use std::cell::{RefCell, RefMut};
use std::rc::Rc;
use util::{project_rect_to_screen, ScreenRect};

#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct ContentAge {
    age: usize,
}

impl ContentAge {
    pub fn new() -> ContentAge {
        ContentAge {
            age: 0,
        }
    }

    pub fn next(&mut self) {
        self.age += 1;
    }
}

pub struct TransformState {
    /// Final, concatenated transform + perspective matrix for this layer
    pub final_transform: Matrix4,

    /// If this is none, the rect was clipped and is not visible at all!
    pub screen_rect: Option<ScreenRect>,

    /// Rectangle in global coordinates, but not transformed.
    pub world_rect: Rect<f32>,

    /// True if this layer has a 3D transform
    pub is_3d: bool,
}

impl TransformState {
    fn new() -> TransformState {
        TransformState {
            final_transform: Matrix4::identity(),
            screen_rect: None,
            world_rect: Rect::zero(),
            is_3d: false,
        }
    }
}

pub struct Layer<T> {
    pub children: RefCell<Vec<Rc<Layer<T>>>>,
    pub transform: RefCell<Matrix4>,
    pub perspective: RefCell<Matrix4>,
    pub tile_size: usize,
    pub extra_data: RefCell<T>,
    tile_grid: RefCell<TileGrid>,

    /// The boundaries of this layer in the coordinate system of the parent layer.
    pub bounds: RefCell<TypedRect<LayerPixel, f32>>,

    /// A monotonically increasing counter that keeps track of the current content age.
    pub content_age: RefCell<ContentAge>,

    /// The content offset for this layer in unscaled layer pixels.
    pub content_offset: RefCell<TypedPoint2D<LayerPixel, f32>>,

    /// Whether this layer clips its children to its boundaries.
    pub masks_to_bounds: RefCell<bool>,

    /// The background color for this layer.
    pub background_color: RefCell<Color>,

    /// The opacity of this layer, from 0.0 (fully transparent) to 1.0 (fully opaque).
    pub opacity: RefCell<f32>,

    /// Whether this stacking context creates a new 3d rendering context.
    pub establishes_3d_context: bool,

    /// Collection of state related to transforms for this layer.
    pub transform_state: RefCell<TransformState>,
}

impl<T> Layer<T> {
    pub fn new(bounds: TypedRect<LayerPixel, f32>,
               tile_size: usize,
               background_color: Color,
               opacity: f32,
               establishes_3d_context: bool,
               data: T)
               -> Layer<T> {
        Layer {
            children: RefCell::new(vec!()),
            transform: RefCell::new(Matrix4::identity()),
            perspective: RefCell::new(Matrix4::identity()),
            bounds: RefCell::new(bounds),
            tile_size: tile_size,
            extra_data: RefCell::new(data),
            tile_grid: RefCell::new(TileGrid::new(tile_size)),
            content_age: RefCell::new(ContentAge::new()),
            masks_to_bounds: RefCell::new(false),
            content_offset: RefCell::new(Point2D::zero()),
            background_color: RefCell::new(background_color),
            opacity: RefCell::new(opacity),
            establishes_3d_context: establishes_3d_context,
            transform_state: RefCell::new(TransformState::new()),
        }
    }

    pub fn children<'a>(&'a self) -> RefMut<'a,Vec<Rc<Layer<T>>>> {
        self.children.borrow_mut()
    }

    pub fn add_child(&self, new_child: Rc<Layer<T>>) {
        self.children().push(new_child);
    }

    pub fn remove_child_at_index(&self, index: usize) {
        self.children().remove(index);
    }

    /// Returns buffer requests inside the given dirty rect, and simultaneously throws out tiles
    /// outside the given viewport rect.
    pub fn get_buffer_requests(&self,
                               rect_in_layer: TypedRect<LayerPixel, f32>,
                               viewport_in_layer: TypedRect<LayerPixel, f32>,
                               scale: ScaleFactor<LayerPixel, DevicePixel, f32>)
                               -> Vec<BufferRequest> {
        let mut tile_grid = self.tile_grid.borrow_mut();
        tile_grid.get_buffer_requests_in_rect(rect_in_layer * scale,
                                              viewport_in_layer * scale,
                                              self.bounds.borrow().size * scale,
                                              &(self.transform_state.borrow().world_rect.origin *
                                                scale.get()),
                                              &self.transform_state.borrow().final_transform,
                                              *self.content_age.borrow())
    }

    pub fn resize(&self, new_size: TypedSize2D<LayerPixel, f32>) {
        self.bounds.borrow_mut().size = new_size;
    }

    pub fn add_buffer(&self, tile: Box<LayerBuffer>) {
        self.tile_grid.borrow_mut().add_buffer(tile);
    }

    pub fn collect_unused_buffers(&self) -> Vec<Box<LayerBuffer>> {
        self.tile_grid.borrow_mut().take_unused_buffers()
    }

    pub fn collect_buffers(&self) -> Vec<Box<LayerBuffer>> {
        self.tile_grid.borrow_mut().collect_buffers()
    }

    pub fn contents_changed(&self) {
        self.content_age.borrow_mut().next();
    }

    pub fn create_textures(&self, display: &NativeDisplay) {
        self.tile_grid.borrow_mut().create_textures(display);
    }

    pub fn do_for_all_tiles<F: FnMut(&Tile)>(&self, f: F) {
        self.tile_grid.borrow().do_for_all_tiles(f);
    }

    pub fn update_transform_state(&self,
                                  parent_transform: &Matrix4,
                                  parent_perspective: &Matrix4,
                                  parent_origin: &Point2D<f32>) {
        let mut ts = self.transform_state.borrow_mut();
        let rect_without_scroll = self.bounds.borrow()
                                             .to_untyped()
                                             .translate(parent_origin);

        ts.world_rect = rect_without_scroll.translate(&self.content_offset.borrow().to_untyped());

        let x0 = ts.world_rect.origin.x;
        let y0 = ts.world_rect.origin.y;

        // Build world space transform
        let local_transform = Matrix4::identity().translate(x0, y0, 0.0)
                                                 .mul(&*self.transform.borrow())
                                                 .translate(-x0, -y0, 0.0);

        ts.final_transform = parent_perspective.mul(&local_transform).mul(&parent_transform);
        ts.screen_rect = project_rect_to_screen(&ts.world_rect, &ts.final_transform);

        // TODO(gw): This is quite bogus. It's a hack to allow the paint task
        // to avoid "optimizing" 3d layers with an incorrect clip rect.
        // We should probably make the display list optimizer work with transforms!
        // This layer is part of a 3d context if its concatenated transform
        // is not identity, since 2d transforms don't get layers.
        ts.is_3d = ts.final_transform != Matrix4::identity();

        // Build world space perspective transform
        let perspective_transform = Matrix4::identity().translate(x0, y0, 0.0)
                                                       .mul(&*self.perspective.borrow())
                                                       .translate(-x0, -y0, 0.0);

        for child in self.children().iter() {
            child.update_transform_state(&ts.final_transform,
                                         &perspective_transform,
                                         &rect_without_scroll.origin);
        }
    }

    /// Calculate the amount of memory used by this layer and all its children.
    /// The memory may be allocated on the heap or in GPU memory.
    pub fn get_memory_usage(&self) -> usize {
        let size_of_children : usize = self.children().iter().map(|ref child| -> usize {
            child.get_memory_usage()
        }).sum();
        size_of_children + self.tile_grid.borrow().get_memory_usage()
    }
}

/// A request from the compositor to the renderer for tiles that need to be (re)displayed.
pub struct BufferRequest {
    /// The rect in pixels that will be drawn to the screen
    pub screen_rect: Rect<usize>,

    /// The rect in page coordinates that this tile represents
    pub page_rect: Rect<f32>,

    /// The content age of that this BufferRequest corresponds to.
    pub content_age: ContentAge,

    /// A cached NativeSurface that can be used to avoid allocating a new one.
    pub native_surface: Option<NativeSurface>,
}

impl BufferRequest {
    pub fn new(screen_rect: Rect<usize>, page_rect: Rect<f32>, content_age: ContentAge)
               -> BufferRequest {
        BufferRequest {
            screen_rect: screen_rect,
            page_rect: page_rect,
            content_age: content_age,
            native_surface: None,
        }
    }
}

pub struct LayerBuffer {
    /// The native surface which can be shared between threads or processes. On Mac this is an
    /// `IOSurface`; on Linux this is an X Pixmap; on Android this is an `EGLImageKHR`.
    pub native_surface: NativeSurface,

    /// The rect in the containing RenderLayer that this represents.
    pub rect: Rect<f32>,

    /// The rect in pixels that will be drawn to the screen.
    pub screen_pos: Rect<usize>,

    /// The scale at which this tile is rendered
    pub resolution: f32,

    /// Whether or not this buffer was painted with the CPU rasterization.
    pub painted_with_cpu: bool,

    /// The content age of that this buffer request corresponds to.
    pub content_age: ContentAge,
}

impl LayerBuffer {
    /// Returns the amount of memory used by the tile
    pub fn get_mem(&self) -> usize {
        self.native_surface.get_memory_usage()
    }

    /// Returns true if the tile is displayable at the given scale
    pub fn is_valid(&self, scale: f32) -> bool {
        (self.resolution - scale).abs() < 1.0e-6
    }

    /// Returns the Size2D of the tile
    pub fn get_size_2d(&self) -> Size2D<usize> {
        self.screen_pos.size
    }

    /// Marks the layer buffer as not leaking. See comments on
    /// `NativeSurfaceMethods::mark_wont_leak` for how this is used.
    pub fn mark_wont_leak(&mut self) {
        self.native_surface.mark_wont_leak()
    }

    /// Destroys the layer buffer. Painting task only.
    pub fn destroy(self, display: &NativeDisplay) {
        let mut this = self;
        this.native_surface.destroy(display)
    }
}

/// A set of layer buffers. This is an atomic unit used to switch between the front and back
/// buffers.
pub struct LayerBufferSet {
    pub buffers: Vec<Box<LayerBuffer>>
}

impl LayerBufferSet {
    /// Notes all buffer surfaces will leak if not destroyed via a call to `destroy`.
    pub fn mark_will_leak(&mut self) {
        for buffer in self.buffers.iter_mut() {
            buffer.native_surface.mark_will_leak()
        }
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use toml::Value;
use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;
use chrono::NaiveDateTime;
use chrono::Local;
use chrono::NaiveDate;

use error::HabitError as HE;
use error::HabitErrorKind as HEK;
use error::*;
use iter::HabitInstanceStoreIdIterator;
use util::date_to_string;
use util::date_from_string;
use util::IsHabitCheck;

use libimagentrylink::internal::InternalLinker;
use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Entry;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagstore::storeid::StoreIdIterator;

/// A HabitTemplate is a "template" of a habit. A user may define a habit "Eat vegetable".
/// If the user ate a vegetable, she should create a HabitInstance from the Habit with the
/// appropriate date (and optionally a comment) set.
pub trait HabitTemplate : Sized {

    /// Create an instance from this habit template
    ///
    /// By default creates an instance with the name of the template, the current time and the
    /// current date and copies the comment from the template to the instance.
    ///
    /// It uses `Store::retrieve()` underneath
    fn create_instance<'a>(&self, store: &'a Store) -> Result<FileLockEntry<'a>>;

    /// Get instances for this template
    fn linked_instances(&self) -> Result<HabitInstanceStoreIdIterator>;

    /// Get the date of the next date when the habit should be done
    fn next_instance_date(&self) -> Result<NaiveDate>;

    /// Check whether the instance is a habit by checking its headers for the habit data
    fn is_habit_template(&self) -> Result<bool>;

    fn habit_name(&self) -> Result<String>;
    fn habit_basedate(&self) -> Result<String>;
    fn habit_recur_spec(&self) -> Result<String>;
    fn habit_comment(&self) -> Result<String>;

    /// Create a StoreId for a habit name and a date the habit should be instantiated for
    fn instance_id_for(habit_name: &String, habit_date: &NaiveDate) -> Result<StoreId>;
}

impl HabitTemplate for Entry {

    fn create_instance<'a>(&self, store: &'a Store) -> Result<FileLockEntry<'a>> {
        let name    = self.habit_name()?;
        let comment = self.habit_comment()?;
        let date    = date_to_string(&Local::today().naive_local());
        let id      = instance_id_for_name_and_datestr(&name, &date)?;

        store.retrieve(id)
            .map_err(From::from)
            .and_then(|mut entry| {
                {
                    let mut hdr = entry.get_header_mut();
                    try!(hdr.insert("habit.instance.name",    Value::String(name)));
                    try!(hdr.insert("habit.instance.date",    Value::String(date)));
                    try!(hdr.insert("habit.instance.comment", Value::String(comment)));
                }
                Ok(entry)
            })
    }

    fn linked_instances(&self) -> Result<HabitInstanceStoreIdIterator> {
        let iter = self
            .get_internal_links()?
            .map(|link| link.get_store_id().clone())
            .filter(IsHabitCheck::is_habit_instance);

        let sidi = StoreIdIterator::new(Box::new(iter));
        Ok(HabitInstanceStoreIdIterator::new(sidi))
    }

    /// Get the date of the next date when the habit should be done
    fn next_instance_date(&self) -> Result<NaiveDate> {
        use kairos::timetype::TimeType;
        use kairos::parser::parse;
        use kairos::parser::Parsed;
        use kairos::iter::extensions::Every;

        let date_from_s = |r: String| -> Result<TimeType> {
            match parse(&r)? {
                Parsed::TimeType(tt) => Ok(tt),
                Parsed::Iterator(_) => {
                    Err(format!("'{}' yields an iterator. Cannot use.", r).into())
                },
            }
        };

        let today = TimeType::today();
        let today = today.get_moment().unwrap(); // we know this is safe.
        debug!("Today is {:?}", today);

        let basedate  = date_from_s(self.habit_basedate()?)?;
        debug!("Basedate is {:?}", today);

        let increment = date_from_s(self.habit_recur_spec()?)?;
        debug!("Increment is {:?}", today);

        for element in basedate.every(increment)? {
            debug!("Calculating: {:?}", element);
            let element = element?.calculate()?;
            if let Some(ndt) = element.get_moment() {
                if ndt > today {
                    debug!("-> {:?} > {:?}", ndt, today);
                    return Ok(ndt.date())
                }
            } else {
                return Err("Iterator seems to return bogus values.".to_owned().into());
            }
        }

        unreachable!() // until we have habit-end-date support
    }

    /// Check whether the instance is a habit by checking its headers for the habit data
    fn is_habit_template(&self) -> Result<bool> {
        [
            "habit.template.name",
            "habit.template.basedate",
            "habit.template.comment",
        ].iter().fold(Ok(true), |acc, path| acc.and_then(|b| {
            self.get_header()
                .read(path)
                .map(|o| is_match!(o, Some(&Value::String(_))))
                .map_err(From::from)
        }))
    }

    fn habit_name(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.name")
    }

    fn habit_basedate(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.basedate")
    }

    fn habit_recur_spec(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.recurspec")
    }

    fn habit_comment(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.comment")
    }

    fn instance_id_for(habit_name: &String, habit_date: &NaiveDate) -> Result<StoreId> {
        instance_id_for_name_and_datestr(habit_name, &date_to_string(habit_date))
    }

}

fn instance_id_for_name_and_datestr(habit_name: &String, habit_date: &String) -> Result<StoreId> {
    use module_path::ModuleEntryPath;

    ModuleEntryPath::new(format!("instance/{}-{}", habit_name, habit_date))
        .into_storeid()
        .map_err(HE::from)
}

#[inline]
fn get_string_header_from_habit(e: &Entry, path: &'static str) -> Result<String> {
    match e.get_header().read(path)? {
        Some(&Value::String(ref s)) => Ok(s.clone()),
        Some(_) => Err(HEK::HeaderTypeError(path, "String").into()),
        None    => Err(HEK::HeaderFieldMissing(path).into()),
    }
}

pub mod builder {
    use toml::Value;
    use toml_query::insert::TomlValueInsertExt;
    use chrono::NaiveDate;

    use libimagstore::store::Store;
    use libimagstore::storeid::StoreId;
    use libimagstore::storeid::IntoStoreId;
    use libimagstore::store::FileLockEntry;

    use error::HabitError as HE;
    use error::HabitErrorKind as HEK;
    use error::*;
    use util::date_to_string;
    use util::date_from_string;

    pub struct HabitBuilder {
        name: Option<String>,
        comment: Option<String>,
        basedate: Option<NaiveDate>,
        recurspec: Option<String>,
    }

    impl HabitBuilder {

        pub fn with_name(mut self, name: String) -> Self {
            self.name = Some(name);
            self
        }

        pub fn with_comment(mut self, comment: String) -> Self {
            self.comment = Some(comment);
            self
        }

        pub fn with_basedate(mut self, date: NaiveDate) -> Self {
            self.basedate = Some(date);
            self
        }

        pub fn with_recurspec(mut self, spec: String) -> Self {
            self.recurspec = Some(spec);
            self
        }

        pub fn build<'a>(self, store: &'a Store) -> Result<FileLockEntry<'a>> {
            #[inline]
            fn mkerr(s: &'static str) -> HE {
                HE::from_kind(HEK::HabitBuilderMissing(s))
            }

            let name      = try!(self.name.ok_or_else(|| mkerr("name")));
            debug!("Success: Name present");

            let dateobj   = try!(self.basedate.ok_or_else(|| mkerr("date")));
            debug!("Success: Date present");

            let recur     = try!(self.recurspec.ok_or_else(|| mkerr("recurspec")));
            debug!("Success: Recurr spec present");

            if let Err(e) = ::kairos::parser::parse(&recur) {
                return Err(e).map_err(From::from);
            }
            let date      = date_to_string(&dateobj);
            debug!("Success: Date valid");

            let comment   = self.comment.unwrap_or_else(|| String::new());
            let sid       = try!(build_habit_template_sid(&name));

            debug!("Creating entry in store for: {:?}", sid);
            let mut entry = try!(store.create(sid));

            try!(entry.get_header_mut().insert("habit.template.name", Value::String(name)));
            try!(entry.get_header_mut().insert("habit.template.basedate", Value::String(date)));
            try!(entry.get_header_mut().insert("habit.template.recurspec", Value::String(recur)));
            try!(entry.get_header_mut().insert("habit.template.comment", Value::String(comment)));

            debug!("Success: Created entry in store and set headers");
            Ok(entry)
        }

    }

    impl Default for HabitBuilder {
        fn default() -> Self {
            HabitBuilder {
                name: None,
                comment: None,
                basedate: None,
                recurspec: None,
            }
        }
    }

    /// Buld a StoreId for a Habit from a date object and a name of a habit
    fn build_habit_template_sid(name: &String) -> Result<StoreId> {
        use module_path::ModuleEntryPath;
        ModuleEntryPath::new(format!("template/{}", name)).into_storeid().map_err(From::from)
    }

}

<commit_msg>Add function to instantiate with a given date<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use toml::Value;
use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;
use chrono::NaiveDateTime;
use chrono::Local;
use chrono::NaiveDate;

use error::HabitError as HE;
use error::HabitErrorKind as HEK;
use error::*;
use iter::HabitInstanceStoreIdIterator;
use util::date_to_string;
use util::date_from_string;
use util::IsHabitCheck;

use libimagentrylink::internal::InternalLinker;
use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Entry;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagstore::storeid::StoreIdIterator;

/// A HabitTemplate is a "template" of a habit. A user may define a habit "Eat vegetable".
/// If the user ate a vegetable, she should create a HabitInstance from the Habit with the
/// appropriate date (and optionally a comment) set.
pub trait HabitTemplate : Sized {

    /// Create an instance from this habit template
    ///
    /// By default creates an instance with the name of the template, the current time and the
    /// current date and copies the comment from the template to the instance.
    ///
    /// It uses `Store::retrieve()` underneath. So if there is already an instance for the day
    /// passed, this will simply return the instance.
    fn create_instance_with_date<'a>(&self, store: &'a Store, date: &NaiveDate)
        -> Result<FileLockEntry<'a>>;

    /// Shortcut for calling `Self::create_instance_with_date()` with an instance of
    /// `::chrono::Local::today().naive_local()`.
    fn create_instance_today<'a>(&self, store: &'a Store) -> Result<FileLockEntry<'a>>;

    /// Get instances for this template
    fn linked_instances(&self) -> Result<HabitInstanceStoreIdIterator>;

    /// Get the date of the next date when the habit should be done
    fn next_instance_date(&self) -> Result<NaiveDate>;

    /// Check whether the instance is a habit by checking its headers for the habit data
    fn is_habit_template(&self) -> Result<bool>;

    fn habit_name(&self) -> Result<String>;
    fn habit_basedate(&self) -> Result<String>;
    fn habit_recur_spec(&self) -> Result<String>;
    fn habit_comment(&self) -> Result<String>;

    /// Create a StoreId for a habit name and a date the habit should be instantiated for
    fn instance_id_for(habit_name: &String, habit_date: &NaiveDate) -> Result<StoreId>;
}

impl HabitTemplate for Entry {

    fn create_instance_with_date<'a>(&self, store: &'a Store, date: &NaiveDate) -> Result<FileLockEntry<'a>> {
        let name    = self.habit_name()?;
        let comment = self.habit_comment()?;
        let date    = date_to_string(date);
        let id      = instance_id_for_name_and_datestr(&name, &date)?;

        store.retrieve(id)
            .map_err(From::from)
            .and_then(|mut entry| {
                {
                    let mut hdr = entry.get_header_mut();
                    hdr.insert("habit.instance.name",    Value::String(name))?;
                    hdr.insert("habit.instance.date",    Value::String(date))?;
                    hdr.insert("habit.instance.comment", Value::String(comment))?;
                }
                Ok(entry)
            })
    }

    fn create_instance_today<'a>(&self, store: &'a Store) -> Result<FileLockEntry<'a>> {
        self.create_instance_with_date(store, &Local::today().naive_local())
    }

    fn linked_instances(&self) -> Result<HabitInstanceStoreIdIterator> {
        let iter = self
            .get_internal_links()?
            .map(|link| link.get_store_id().clone())
            .filter(IsHabitCheck::is_habit_instance);

        let sidi = StoreIdIterator::new(Box::new(iter));
        Ok(HabitInstanceStoreIdIterator::new(sidi))
    }

    /// Get the date of the next date when the habit should be done
    fn next_instance_date(&self) -> Result<NaiveDate> {
        use kairos::timetype::TimeType;
        use kairos::parser::parse;
        use kairos::parser::Parsed;
        use kairos::iter::extensions::Every;

        let date_from_s = |r: String| -> Result<TimeType> {
            match parse(&r)? {
                Parsed::TimeType(tt) => Ok(tt),
                Parsed::Iterator(_) => {
                    Err(format!("'{}' yields an iterator. Cannot use.", r).into())
                },
            }
        };

        let today = TimeType::today();
        let today = today.get_moment().unwrap(); // we know this is safe.
        debug!("Today is {:?}", today);

        let basedate  = date_from_s(self.habit_basedate()?)?;
        debug!("Basedate is {:?}", today);

        let increment = date_from_s(self.habit_recur_spec()?)?;
        debug!("Increment is {:?}", today);

        for element in basedate.every(increment)? {
            debug!("Calculating: {:?}", element);
            let element = element?.calculate()?;
            if let Some(ndt) = element.get_moment() {
                if ndt > today {
                    debug!("-> {:?} > {:?}", ndt, today);
                    return Ok(ndt.date())
                }
            } else {
                return Err("Iterator seems to return bogus values.".to_owned().into());
            }
        }

        unreachable!() // until we have habit-end-date support
    }

    /// Check whether the instance is a habit by checking its headers for the habit data
    fn is_habit_template(&self) -> Result<bool> {
        [
            "habit.template.name",
            "habit.template.basedate",
            "habit.template.comment",
        ].iter().fold(Ok(true), |acc, path| acc.and_then(|b| {
            self.get_header()
                .read(path)
                .map(|o| is_match!(o, Some(&Value::String(_))))
                .map_err(From::from)
        }))
    }

    fn habit_name(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.name")
    }

    fn habit_basedate(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.basedate")
    }

    fn habit_recur_spec(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.recurspec")
    }

    fn habit_comment(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.comment")
    }

    fn instance_id_for(habit_name: &String, habit_date: &NaiveDate) -> Result<StoreId> {
        instance_id_for_name_and_datestr(habit_name, &date_to_string(habit_date))
    }

}

fn instance_id_for_name_and_datestr(habit_name: &String, habit_date: &String) -> Result<StoreId> {
    use module_path::ModuleEntryPath;

    ModuleEntryPath::new(format!("instance/{}-{}", habit_name, habit_date))
        .into_storeid()
        .map_err(HE::from)
}

#[inline]
fn get_string_header_from_habit(e: &Entry, path: &'static str) -> Result<String> {
    match e.get_header().read(path)? {
        Some(&Value::String(ref s)) => Ok(s.clone()),
        Some(_) => Err(HEK::HeaderTypeError(path, "String").into()),
        None    => Err(HEK::HeaderFieldMissing(path).into()),
    }
}

pub mod builder {
    use toml::Value;
    use toml_query::insert::TomlValueInsertExt;
    use chrono::NaiveDate;

    use libimagstore::store::Store;
    use libimagstore::storeid::StoreId;
    use libimagstore::storeid::IntoStoreId;
    use libimagstore::store::FileLockEntry;

    use error::HabitError as HE;
    use error::HabitErrorKind as HEK;
    use error::*;
    use util::date_to_string;
    use util::date_from_string;

    pub struct HabitBuilder {
        name: Option<String>,
        comment: Option<String>,
        basedate: Option<NaiveDate>,
        recurspec: Option<String>,
    }

    impl HabitBuilder {

        pub fn with_name(mut self, name: String) -> Self {
            self.name = Some(name);
            self
        }

        pub fn with_comment(mut self, comment: String) -> Self {
            self.comment = Some(comment);
            self
        }

        pub fn with_basedate(mut self, date: NaiveDate) -> Self {
            self.basedate = Some(date);
            self
        }

        pub fn with_recurspec(mut self, spec: String) -> Self {
            self.recurspec = Some(spec);
            self
        }

        pub fn build<'a>(self, store: &'a Store) -> Result<FileLockEntry<'a>> {
            #[inline]
            fn mkerr(s: &'static str) -> HE {
                HE::from_kind(HEK::HabitBuilderMissing(s))
            }

            let name      = try!(self.name.ok_or_else(|| mkerr("name")));
            debug!("Success: Name present");

            let dateobj   = try!(self.basedate.ok_or_else(|| mkerr("date")));
            debug!("Success: Date present");

            let recur     = try!(self.recurspec.ok_or_else(|| mkerr("recurspec")));
            debug!("Success: Recurr spec present");

            if let Err(e) = ::kairos::parser::parse(&recur) {
                return Err(e).map_err(From::from);
            }
            let date      = date_to_string(&dateobj);
            debug!("Success: Date valid");

            let comment   = self.comment.unwrap_or_else(|| String::new());
            let sid       = try!(build_habit_template_sid(&name));

            debug!("Creating entry in store for: {:?}", sid);
            let mut entry = try!(store.create(sid));

            try!(entry.get_header_mut().insert("habit.template.name", Value::String(name)));
            try!(entry.get_header_mut().insert("habit.template.basedate", Value::String(date)));
            try!(entry.get_header_mut().insert("habit.template.recurspec", Value::String(recur)));
            try!(entry.get_header_mut().insert("habit.template.comment", Value::String(comment)));

            debug!("Success: Created entry in store and set headers");
            Ok(entry)
        }

    }

    impl Default for HabitBuilder {
        fn default() -> Self {
            HabitBuilder {
                name: None,
                comment: None,
                basedate: None,
                recurspec: None,
            }
        }
    }

    /// Buld a StoreId for a Habit from a date object and a name of a habit
    fn build_habit_template_sid(name: &String) -> Result<StoreId> {
        use module_path::ModuleEntryPath;
        ModuleEntryPath::new(format!("template/{}", name)).into_storeid().map_err(From::from)
    }

}

<|endoftext|>