<commit_before>#![allow(dead_code, unused_must_use, unused_imports, unstable)]

extern crate "rustc-serialize" as rustc_serialize;
extern crate log;

use utils;
use std::fmt;
use std::str;
use std::string;
use std::ops::Drop;
use std::old_io::{File, Open, Append, Read, Write, ReadWrite};
use std::old_io::TempDir;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;
use std::old_io::{BufferedReader, BufferedWriter};
use std::old_path::BytesContainer;
use self::rustc_serialize::base64::{STANDARD, FromBase64, ToBase64};

/// A result type that's specfici to the Reader module.
/// TODO Decide if this is necessary
pub type ReaderResult<T, E> = Result<T, E>;

/// Reader struct of its basic properties.
pub struct Reader {
    /// Path to file where the Reader is created.
    path: Path,
    /// BufferedReader for reading the file. Initialized with the Path.
    read_buffer: BufferedReader<File>,
    /// BufferedWriter for writing to the file. Initialized with the Path.
    write_buffer: BufferedWriter<File>,
    /// Index counter to know how many records exist.
    id_count: u64,
}

/// ReaderFile traits
pub trait ReaderFile {
    // Opens a new File to the Path provided.
    // Returns a boxed File.
    fn open(&self) -> File;
    // Inserts a string to the database.
    fn insert_string(&mut self, String);
}

impl fmt::Debug for Reader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Reader: ( path: {} )", self.path.display())
    }
}

impl ToString for Reader {
    fn to_string(&self) -> String {
        format!("{:?}", self)
    }
}

impl Reader {
    /// Creates a new Reader from the Path provided.
    /// Opens a new BufferedReader and BufferedWriter (with Append mode) to the file.
    /// If the file doesn't exist, it is created.
    // TODO, create a .lock file to let other readers know the database is in use (see: #2).
    pub fn new(apath: &Path) -> Reader {
        Reader::file_lock_create(apath);
        // if file_lock exists, panic and crash with appropriate error.
        // Error: A .lock file already exists, if this is from a previous session, consider
        // deleting the .lock file.

        if !apath.exists() {
            let mut file = File::create(&apath.clone());
            file.write_line("0");
            file.flush();
        }

        let mut buffer_writer = match File::open_mode(&apath.clone(), Append, ReadWrite) {
                                    Ok(file)    => { BufferedWriter::new(file) },
                                    Err(..)     => {
                                        panic!("Failed to create a write buffer to file path: {}",
                                                apath.display())
                                    },
                                };

        let mut buffer_reader = match File::open_mode(&apath.clone(), Open, Read) {
                                    Ok(file)    => BufferedReader::new(file),
                                    Err(..)     => panic!("Failed to create a read buffer to file path: {}",
                                                            apath.display()),
                                };

        let current_record_count = match buffer_reader.read_line() {
                                        Ok(line)    => {
                                            let first_line = utils::string_slice(line);
                                            let input = first_line[0].trim().parse::<u64>();
                                            match input {
                                                Ok(num) => num,
                                                Err(..) => {
                                                    warn!("We can't parse the record count from metadata");
                                                    warn!("Our parsed db metadata: {:?}", first_line);
                                                    // Setting counter to zero because we can't do
                                                    // anything else.
                                                    0
                                                },
                                            }
                                        },
                                        Err(..) => {
                                            error!("Can't read database metadata! Everybody do the flop!");
                                            // Setting counter to zero because we can't do anything
                                            // else.
                                            0
                                        },
                                   };

        Reader {
            path: apath.clone(),
            read_buffer: buffer_reader,
            write_buffer: buffer_writer,
            id_count: current_record_count,
        }
    }

    /// This is a helder function that realistically shouldn't exist in production.
    /// Used primarily for "spilling" the entire database file into a Vec<String>
    fn spill(&mut self) -> Vec<String> {
        let mut result: Vec<String> = Vec::new();
        let mut buffer_reader = BufferedReader::new(File::open(&self.path.clone()));
        for line_iter in buffer_reader.lines() {
            result.push(line_iter.unwrap().trim().to_string());
        }
        return result;
    }

    /// Inserts a &str into the database.
    pub fn insert_str(&mut self, item: &str) {
        self.write_buffer.write_line(item);
        self.write_buffer.flush();
        self.id_count = self.id_count + 1;
        self.update_counter(self.id_count);
    }

    /// Inserts a byte array into the database.
    pub fn insert(&mut self, item: &[u8]) {
        self.write_buffer.write(item);
        self.write_buffer.flush();
        self.id_count = self.id_count + 1;
        self.update_counter(self.id_count);
    }

    /// Read a &str from the database
    fn read_line(&mut self) -> String {
        match self.read_buffer.read_line() {
            Ok(string)  => { string.to_string() },
            Err(..)     => {
                error!("Unable to read next line. BufferedReader error.");
                "".to_string()
            },
        }
    }

    /// Creates a .lock file to let other processes know that the database is in use.
    /// This is still unfinished and should be considered broken.
    fn file_lock_create(lockpath: &Path) -> (bool, Path) {
        if lockpath.exists() {
            return (true, lockpath.clone())
        }

        let mut filelock_path = lockpath.clone();

        // Remove the old name
        filelock_path.pop();
        // Surely, there's a less ugly way to take the filename of a Path and convert it to a string?!
        let mut filename_lock: String = str::from_utf8(lockpath.filename().unwrap()).unwrap().to_string();
        filename_lock.push_str(".lock");
        // Join the new filename with the path
        filelock_path = filelock_path.join(filename_lock);
        println!("{}", filelock_path.display());
        match File::create(&filelock_path) {
            Ok(..)  => (true, filelock_path),
            Err(..) => (false, filelock_path),
        }
    }

    /// Removes .lock file when the reader process is completed.
    fn file_lock_remove(&self, filelock: &Path) -> bool {
        fs::unlink(&filelock.clone());
        filelock.exists()
    }

    /// Updates database counter on disk.
    fn update_counter(&self, value: u64) {
        let file = File::open_mode(&self.path.clone(), Open, Write);
        let mut buffer_writer = BufferedWriter::new(file);
        buffer_writer.write_line(value.to_string().as_slice());
        buffer_writer.flush();
    }

}

impl Drop for Reader {
    fn drop(&mut self) {
        let mut lock_path = self.path.clone();
        let mut filename = String::from_str(lock_path.filename_str().unwrap());
        filename.push_str(".lock");
        lock_path.set_filename(filename);
        println!("File about to remove: {}", lock_path.display());
        self.file_lock_remove(&lock_path);
    }
}

impl ReaderFile for Reader {

    fn open(&self) -> File {
        match File::open_mode(&self.path, Open, ReadWrite) {
            Ok(file)    => file,
            Err(..)     => { panic!("File {} couldn't be opened!", &self.path.display()); },
        }
    }

    fn insert_string(&mut self, item: String) {
        self.insert_str(&*item);
    }

}

#[test]
fn test_open_file() {
    let reader = Reader::new(&Path::new("tests/base-test.txt"));
}

#[test]
fn test_create_file() {
    use std::rand;
    let mut path_str = String::from_str("tests/");
    path_str.push_str(&*rand::random::<usize>().to_string());
    path_str.push_str(".txt");

    let (tempdir, apath) = setup();
    let path = tempdir.path().join(rand::random::<usize>().to_string());

    //let path = Path::new(path_str);
    assert!(!path.exists());
    let reader = Reader::new(&path.clone());
    assert!(path.exists());
    fs::unlink(&path);
}

#[test]
fn test_read_file() {
    // We should output the entire contents of the database file we open
    // into standard output.
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["2".to_string(), "10 11".to_string(), "20 21".to_string()];
    assert_eq!(expected, reader.spill());
}

#[test]
fn test_write_string_to_file() {
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["3".to_string(), "10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_string("30 31".to_string());
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_write_str_to_file() {
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["3".to_string(), "10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_str("30 31");
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_file_path_lock() {

    let (tempdir, path) = setup();

    let mut expected = path.clone();
    expected.pop();

    // Surely, there's a less ugly way to take the filename of a Path and convert it to a string?!
    let mut filename_lock: String = str::from_utf8(path.filename().unwrap()).unwrap().to_string();
    filename_lock.push_str(".lock");
    expected = expected.join(filename_lock);

    let reader = Reader::new(&expected.clone());
    assert!(expected.exists() && expected.is_file());
}

#[test]
fn test_reader_show() {
    let reader: Reader = Reader::new(&Path::new("tests/file.txt"));
    assert_eq!("Reader: ( path: tests/file.txt )", reader.to_string());
}

/// Test setup code. Current functions:
/// - Create a new file with `TempDir` and a random name.
/// - Write a 2x2 matrix of records into the base-test.txt file
/// - Returns a tuple of `TempDir` and `Path` to the file.
/// - The path is for r/w access and `TempDir` is so that the directory
///   isn't deleted before the test is completed.
#[allow(dead_code, unused_must_use)]
fn setup() -> (TempDir, Path) {
    use std::rand;

    let tmpdir = match TempDir::new("txtdb-tests") {
        Ok(dir) => dir,
        Err(..) => panic!("Cannot create test directory. Tests will fail."),
    };

    let final_dir = tmpdir.path().join(rand::random::<usize>().to_string());

    let mut file = File::create(&final_dir.clone());
    file.write_str("2\n10 11\n20 21\n");

    (tmpdir, final_dir)
}
<commit_msg>Removed unnecessary pub in writes<commit_after>#![allow(dead_code, unused_must_use, unused_imports, unstable)]

extern crate "rustc-serialize" as rustc_serialize;
extern crate log;

use utils;
use std::fmt;
use std::str;
use std::string;
use std::ops::Drop;
use std::old_io::{File, Open, Append, Read, Write, ReadWrite};
use std::old_io::TempDir;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;
use std::old_io::{BufferedReader, BufferedWriter};
use std::old_path::BytesContainer;
use self::rustc_serialize::base64::{STANDARD, FromBase64, ToBase64};

/// A result type that's specfici to the Reader module.
/// TODO Decide if this is necessary
pub type ReaderResult<T, E> = Result<T, E>;

/// Reader struct of its basic properties.
pub struct Reader {
    /// Path to file where the Reader is created.
    path: Path,
    /// BufferedReader for reading the file. Initialized with the Path.
    read_buffer: BufferedReader<File>,
    /// BufferedWriter for writing to the file. Initialized with the Path.
    write_buffer: BufferedWriter<File>,
    /// Index counter to know how many records exist.
    id_count: u64,
}

/// ReaderFile traits
pub trait ReaderFile {
    // Opens a new File to the Path provided.
    // Returns a boxed File.
    fn open(&self) -> File;
    // Inserts a string to the database.
    fn insert_string(&mut self, String);
}

impl fmt::Debug for Reader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Reader: ( path: {} )", self.path.display())
    }
}

impl ToString for Reader {
    fn to_string(&self) -> String {
        format!("{:?}", self)
    }
}

impl Reader {
    /// Creates a new Reader from the Path provided.
    /// Opens a new BufferedReader and BufferedWriter (with Append mode) to the file.
    /// If the file doesn't exist, it is created.
    // TODO, create a .lock file to let other readers know the database is in use (see: #2).
    pub fn new(apath: &Path) -> Reader {
        Reader::file_lock_create(apath);
        // if file_lock exists, panic and crash with appropriate error.
        // Error: A .lock file already exists, if this is from a previous session, consider
        // deleting the .lock file.

        if !apath.exists() {
            let mut file = File::create(&apath.clone());
            file.write_line("0");
            file.flush();
        }

        let mut buffer_writer = match File::open_mode(&apath.clone(), Append, ReadWrite) {
                                    Ok(file)    => { BufferedWriter::new(file) },
                                    Err(..)     => {
                                        panic!("Failed to create a write buffer to file path: {}",
                                                apath.display())
                                    },
                                };

        let mut buffer_reader = match File::open_mode(&apath.clone(), Open, Read) {
                                    Ok(file)    => BufferedReader::new(file),
                                    Err(..)     => panic!("Failed to create a read buffer to file path: {}",
                                                            apath.display()),
                                };

        let current_record_count = match buffer_reader.read_line() {
                                        Ok(line)    => {
                                            let first_line = utils::string_slice(line);
                                            let input = first_line[0].trim().parse::<u64>();
                                            match input {
                                                Ok(num) => num,
                                                Err(..) => {
                                                    warn!("We can't parse the record count from metadata");
                                                    warn!("Our parsed db metadata: {:?}", first_line);
                                                    // Setting counter to zero because we can't do
                                                    // anything else.
                                                    0
                                                },
                                            }
                                        },
                                        Err(..) => {
                                            error!("Can't read database metadata! Everybody do the flop!");
                                            // Setting counter to zero because we can't do anything
                                            // else.
                                            0
                                        },
                                   };

        Reader {
            path: apath.clone(),
            read_buffer: buffer_reader,
            write_buffer: buffer_writer,
            id_count: current_record_count,
        }
    }

    /// This is a helder function that realistically shouldn't exist in production.
    /// Used primarily for "spilling" the entire database file into a Vec<String>
    fn spill(&mut self) -> Vec<String> {
        let mut result: Vec<String> = Vec::new();
        let mut buffer_reader = BufferedReader::new(File::open(&self.path.clone()));
        for line_iter in buffer_reader.lines() {
            result.push(line_iter.unwrap().trim().to_string());
        }
        return result;
    }

    /// Inserts a &str into the database.
    fn insert_str(&mut self, item: &str) {
        self.write_buffer.write_line(item);
        self.write_buffer.flush();
        self.id_count = self.id_count + 1;
        self.update_counter(self.id_count);
    }

    /// Inserts a byte array into the database.
    fn insert(&mut self, item: &[u8]) {
        self.write_buffer.write(item);
        self.write_buffer.flush();
        self.id_count = self.id_count + 1;
        self.update_counter(self.id_count);
    }

    /// Read a &str from the database
    fn read_line(&mut self) -> String {
        match self.read_buffer.read_line() {
            Ok(string)  => { string.to_string() },
            Err(..)     => {
                error!("Unable to read next line. BufferedReader error.");
                "".to_string()
            },
        }
    }

    /// Creates a .lock file to let other processes know that the database is in use.
    /// This is still unfinished and should be considered broken.
    fn file_lock_create(lockpath: &Path) -> (bool, Path) {
        if lockpath.exists() {
            return (true, lockpath.clone())
        }

        let mut filelock_path = lockpath.clone();

        // Remove the old name
        filelock_path.pop();
        // Surely, there's a less ugly way to take the filename of a Path and convert it to a string?!
        let mut filename_lock: String = str::from_utf8(lockpath.filename().unwrap()).unwrap().to_string();
        filename_lock.push_str(".lock");
        // Join the new filename with the path
        filelock_path = filelock_path.join(filename_lock);
        println!("{}", filelock_path.display());
        match File::create(&filelock_path) {
            Ok(..)  => (true, filelock_path),
            Err(..) => (false, filelock_path),
        }
    }

    /// Removes .lock file when the reader process is completed.
    fn file_lock_remove(&self, filelock: &Path) -> bool {
        fs::unlink(&filelock.clone());
        filelock.exists()
    }

    /// Updates database counter on disk.
    fn update_counter(&self, value: u64) {
        let file = File::open_mode(&self.path.clone(), Open, Write);
        let mut buffer_writer = BufferedWriter::new(file);
        buffer_writer.write_line(value.to_string().as_slice());
        buffer_writer.flush();
    }

}

impl Drop for Reader {
    fn drop(&mut self) {
        let mut lock_path = self.path.clone();
        let mut filename = String::from_str(lock_path.filename_str().unwrap());
        filename.push_str(".lock");
        lock_path.set_filename(filename);
        println!("File about to remove: {}", lock_path.display());
        self.file_lock_remove(&lock_path);
    }
}

impl ReaderFile for Reader {

    fn open(&self) -> File {
        match File::open_mode(&self.path, Open, ReadWrite) {
            Ok(file)    => file,
            Err(..)     => { panic!("File {} couldn't be opened!", &self.path.display()); },
        }
    }

    fn insert_string(&mut self, item: String) {
        self.insert_str(&*item);
    }

}

#[test]
fn test_open_file() {
    let reader = Reader::new(&Path::new("tests/base-test.txt"));
}

#[test]
fn test_create_file() {
    use std::rand;
    let mut path_str = String::from_str("tests/");
    path_str.push_str(&*rand::random::<usize>().to_string());
    path_str.push_str(".txt");

    let (tempdir, apath) = setup();
    let path = tempdir.path().join(rand::random::<usize>().to_string());

    //let path = Path::new(path_str);
    assert!(!path.exists());
    let reader = Reader::new(&path.clone());
    assert!(path.exists());
    fs::unlink(&path);
}

#[test]
fn test_read_file() {
    // We should output the entire contents of the database file we open
    // into standard output.
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["2".to_string(), "10 11".to_string(), "20 21".to_string()];
    assert_eq!(expected, reader.spill());
}

#[test]
fn test_write_string_to_file() {
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["3".to_string(), "10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_string("30 31".to_string());
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_write_str_to_file() {
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["3".to_string(), "10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_str("30 31");
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_file_path_lock() {

    let (tempdir, path) = setup();

    let mut expected = path.clone();
    expected.pop();

    // Surely, there's a less ugly way to take the filename of a Path and convert it to a string?!
    let mut filename_lock: String = str::from_utf8(path.filename().unwrap()).unwrap().to_string();
    filename_lock.push_str(".lock");
    expected = expected.join(filename_lock);

    let reader = Reader::new(&expected.clone());
    assert!(expected.exists() && expected.is_file());
}

#[test]
fn test_reader_show() {
    let reader: Reader = Reader::new(&Path::new("tests/file.txt"));
    assert_eq!("Reader: ( path: tests/file.txt )", reader.to_string());
}

/// Test setup code. Current functions:
/// - Create a new file with `TempDir` and a random name.
/// - Write a 2x2 matrix of records into the base-test.txt file
/// - Returns a tuple of `TempDir` and `Path` to the file.
/// - The path is for r/w access and `TempDir` is so that the directory
///   isn't deleted before the test is completed.
#[allow(dead_code, unused_must_use)]
fn setup() -> (TempDir, Path) {
    use std::rand;

    let tmpdir = match TempDir::new("txtdb-tests") {
        Ok(dir) => dir,
        Err(..) => panic!("Cannot create test directory. Tests will fail."),
    };

    let final_dir = tmpdir.path().join(rand::random::<usize>().to_string());

    let mut file = File::create(&final_dir.clone());
    file.write_str("2\n10 11\n20 21\n");

    (tmpdir, final_dir)
}
<|endoftext|>


<commit_before>//! Types around a convolution, see also https://en.wikipedia.org/wiki/Convolution.
//!
//! Convolutions in this library can be defined in time or frequency domain. In
//! frequency domain the convolution is automatically transformed into a multiplication
//! which is the analog operation to a convolution in time domain.
use RealNumber;
use num::traits::Zero;
use num::complex::{Complex, Complex32,Complex64};
use vector_types::{
    RealTimeVector,
    ComplexTimeVector,
    ComplexFreqVector
};
use vector_types::definitions::{
    DataVector,
    RealVectorOperations,
    ComplexVectorOperations
};

use vector_types::time_freq_impl::{
    TimeDomainOperations,
    FrequencyDomainOperations
};

/// A convolution function in time domain and real number space
pub trait RealTimeConvFunction<T>
    where T: RealNumber {
    fn calc(&self, x: T) -> T;
}

/// A convolution function in time domain and complex number space
pub trait ComplexTimeConvFunction<T>
    where T: RealNumber {
    fn calc(&self, x: T) -> Complex<T>;
}

/// A convolution function in frequency domain and complex number space
pub trait ComplexFrequencyConvFunction<T>
    where T: RealNumber {
    fn calc(&self, x: T) -> Complex<T>;
}

/// Allows to create a lookup table with linear interpolation between table points.
/// This usually speeds up a convolution and sacrifies accuracy.
pub struct RealTimeLinearTableLookup<T>
    where T: RealNumber {
    table: Vec<T>,
    delta: T,
    offset: T
}

/// Allows to create a lookup table with linear interpolation between table points.
/// This usually speeds up a convolution and sacrifies accuracy.
pub struct ComplexTimeLinearTableLookup<T>
    where T: RealNumber {
    table: Vec<Complex<T>>,
    delta: T,
    offset: T
}

/// Allows to create a lookup table with linear interpolation between table points.
/// This usually speeds up a convolution and sacrifies accuracy.
pub struct ComplexFrequencyLinearTableLookup<T>
    where T: RealNumber {
    table: Vec<Complex<T>>,
    delta: T,
    offset: T
}

macro_rules! add_linear_table_lookup_impl {
    ($($name: ident: $conv_type: ident, $($data_type: ident, $result_type:ident),*);*) => {
        $(
            $(
                impl $conv_type<$data_type> for $name<$data_type> {
                    fn calc(&self, x: $data_type) -> $result_type {
                        let len = self.table.len();
                        let center = len / 2;
                        let center_float = center as $data_type;
                        let x = x / self.delta - self.offset + center_float;
                        let round_float = x.round();
                        let round = round_float as usize;
                        if round >= len {
                            return $result_type::zero();
                        }

                        let round_tolerance = 1e-6;
                        if (x - round_float).abs() < round_tolerance {
                            return self.table[round];
                        }

                        if x > round_float {
                            let other = round + 1;
                            if other >= len {
                                return self.table[round];
                            }
                            let y0 = self.table[round];
                            let x0 = round_float;
                            let y1 = self.table[other];
                            return y0 + (y1 - y0) * (x - x0);
                        } else {
                            if round == 0 {
                                return self.table[round];
                            }
                            let other = round - 1;
                            let y0 = self.table[round];
                            let x0 = round_float;
                            let y1 = self.table[other];
                            return y0 + (y1 - y0) * (x0 - x);
                        }
                    }
                }

                impl $name<$data_type> {
                    /// Creates a lookup table by putting the pieces together.
                    pub fn from_raw_parts(table: &[$result_type], delta: $data_type, offset: $data_type) -> Self {
                        let owned_table = Vec::from(table);
                        $name { table: owned_table, delta: delta, offset: offset }
                    }

                    /// Creates a lookup table from another convolution function. The `delta` argument
                    /// can be used to balance performance vs. accuracy.
                    pub fn from_conv_function(other: &$conv_type<$data_type>, delta: $data_type, offset: $data_type, len: usize) -> Self {
                        let center = (len / 2) as isize;
                        let mut table = vec![$result_type::zero(); len];
                        let mut i = -center;
                        for n in &mut table {
                            *n = other.calc((i as $data_type) * delta);
                            i += 1;
                        }
                        $name { table: table, delta: delta, offset: offset }
                    }
                }
            )*
        )*
    }
}
add_linear_table_lookup_impl!(
    RealTimeLinearTableLookup: RealTimeConvFunction, f32, f32, f64, f64;
    ComplexTimeLinearTableLookup: ComplexTimeConvFunction, f32, Complex32, f64, Complex64;
    ComplexFrequencyLinearTableLookup: ComplexFrequencyConvFunction, f32, Complex32, f64, Complex64);

macro_rules! add_real_time_linear_table_impl {
    ($($data_type: ident),*) => {
        $(
            impl RealTimeLinearTableLookup<$data_type> {
                /// Convert the lookup table into complex number space
                pub fn to_complex(&self) -> ComplexTimeLinearTableLookup<$data_type> {
                    let vector = RealTimeVector::from_array(&self.table);
                    let complex = vector.to_complex().expect("to_complex shouldn't fail");
                    let complex = complex.complex_data();
                    let mut table = Vec::with_capacity(complex.len());
                    for n in complex {
                        table.push(*n);
                    }
                    ComplexTimeLinearTableLookup { table: table, delta: self.delta, offset: self.offset }
                }
            }
        )*
    }
}
add_real_time_linear_table_impl!(f32, f64);

macro_rules! add_complex_time_linear_table_impl {
    ($($data_type: ident),*) => {
        $(
            impl ComplexTimeLinearTableLookup<$data_type> {
                /// Convert the lookup table into real number space
                pub fn to_real(self) -> RealTimeLinearTableLookup<$data_type> {
                    let vector = ComplexTimeVector::from_complex(&self.table);
                    let real = vector.to_real().expect("to_complex shouldn't fail");
                    let real = real.data();
                    let mut table = Vec::with_capacity(real.len());
                    for n in real {
                        table.push(*n);
                    }
                    RealTimeLinearTableLookup { table: table, delta: self.delta, offset: self.offset }
                }

                /// Convert the lookup table into frequency domain
                pub fn fft(self) -> ComplexFrequencyLinearTableLookup<$data_type> {
                    let vector = ComplexTimeVector::from_complex(&self.table);
                    let freq = vector.fft().expect("vector fft shouldn't fail");
                    let freq = freq.complex_data();
                    let mut table = Vec::with_capacity(freq.len());
                    for n in freq {
                        table.push(*n);
                    }
                    ComplexFrequencyLinearTableLookup { table: table, delta: self.delta, offset: self.offset }
                }
            }
        )*
    }
}
add_complex_time_linear_table_impl!(f32, f64);

macro_rules! add_complex_frequency_linear_table_impl {
    ($($data_type: ident),*) => {
        $(
            impl ComplexFrequencyLinearTableLookup<$data_type> {

                /// Convert the lookup table into time domain
                pub fn ifft(self) -> ComplexTimeLinearTableLookup<$data_type> {
                    let vector = ComplexFreqVector::from_complex(&self.table);
                    let time = vector.ifft().expect("vector ifft shouldn't fail");
                    let time = time.complex_data();
                    let mut table = Vec::with_capacity(time.len());
                    for n in time {
                        table.push(*n);
                    }
                    ComplexTimeLinearTableLookup { table: table, delta: self.delta, offset: self.offset }
                }
            }
        )*
    }
}
add_complex_frequency_linear_table_impl!(f32, f64);

/// Raised cosine function according to https://en.wikipedia.org/wiki/Raised-cosine_filter
pub struct  RaiseCosineFuncton<T>
    where T: RealNumber {
    rolloff: T
}

impl<T> RealTimeConvFunction<T> for RaiseCosineFuncton<T>
    where T: RealNumber {
    fn calc(&self, x: T) -> T {
        if x == T::zero() {
            return T::one();
        }

        let one = T::one();
        let two = T::from(2.0).unwrap();
        let pi = two * one.asin();
        let four = two * two;
        if x.abs() == one / (two * self.rolloff) {
            let arg = pi / two / self.rolloff;
            return (arg).sin() / arg * pi / four;
        }

        let pi_x = pi * x;
        let arg = two * self.rolloff * x;
        return pi_x.sin() * (pi_x * self.rolloff).cos() / pi_x / (one - (arg * arg))
    }
}

impl<T> RaiseCosineFuncton<T>
    where T: RealNumber {
    pub fn new(rolloff: T) -> Self {
        RaiseCosineFuncton { rolloff: rolloff }
    }
}

#[cfg(test)]
mod tests {
	use super::*;
    use super::super::RealNumber;
    use std::fmt::Debug;

    fn conv_test<T, C>(conv: C, expected: &[T], step: T, tolerance: T)
        where T: RealNumber + Debug,
              C: RealTimeConvFunction<T> {
        let mut result = vec![T::zero(); expected.len()];
        let mut j = -(expected.len() as isize / 2);
        for i in 0..result.len() {
            result[i] = conv.calc(T::from(j).unwrap() * step);
            j += 1;
        }

        for i in 0..result.len() {
            if (result[i] - expected[i]).abs() > tolerance {
                panic!("assertion failed: {:?} != {:?}", result, expected);
            }
        }
    }

	#[test]
	fn raised_cosine32_test()
	{
        let window = RaiseCosineFuncton::new(0.35);
        let expected =
            [0.0, 0.2171850639713355, 0.4840621929215732, 0.7430526238101408, 0.9312114164253432,
             1.0, 0.9312114164253432, 0.7430526238101408, 0.4840621929215732, 0.2171850639713355];
        conv_test(window, &expected, 0.2, 1e-4);
	}

    #[test]
    fn lookup_table32_test() {
        let window = RaiseCosineFuncton::new(0.35);
        let table = RealTimeLinearTableLookup::<f64>::from_conv_function(&window, 0.2, 0.0, 10);
        let expected =
            [0.0, 0.2171850639713355, 0.4840621929215732, 0.7430526238101408, 0.9312114164253432,
             1.0, 0.9312114164253432, 0.7430526238101408, 0.4840621929215732, 0.2171850639713355];
        conv_test(table, &expected, 0.2, 1e-4);
    }

    #[test]
    fn linear_interpolation_lookup_table32_test() {
        let window = RaiseCosineFuncton::new(0.35);
        let table = RealTimeLinearTableLookup::<f64>::from_conv_function(&window, 0.4, 0.0, 10);
        let expected =
            [0.0, 0.2171850639713355, 0.4840621929215732, 0.7430526238101408, 0.9312114164253432,
             1.0, 0.9312114164253432, 0.7430526238101408, 0.4840621929215732, 0.2171850639713355];
        conv_test(table, &expected, 0.2, 0.1);
    }
}<commit_msg>Added documentation<commit_after>//! Types around a convolution, see also https://en.wikipedia.org/wiki/Convolution.
//!
//! Convolutions in this library can be defined in time or frequency domain. In
//! frequency domain the convolution is automatically transformed into a multiplication
//! which is the analog operation to a convolution in time domain.
use RealNumber;
use num::traits::Zero;
use num::complex::{Complex, Complex32,Complex64};
use vector_types::{
    RealTimeVector,
    ComplexTimeVector,
    ComplexFreqVector
};
use vector_types::definitions::{
    DataVector,
    RealVectorOperations,
    ComplexVectorOperations
};

use vector_types::time_freq_impl::{
    TimeDomainOperations,
    FrequencyDomainOperations
};

/// A convolution function in time domain and real number space
pub trait RealTimeConvFunction<T>
    where T: RealNumber {
    /// Calculates the convolution for a real data point
    fn calc(&self, x: T) -> T;
}

/// A convolution function in time domain and complex number space
pub trait ComplexTimeConvFunction<T>
    where T: RealNumber {
    /// Calculates the convolution for a real data point
    fn calc(&self, x: T) -> Complex<T>;
}

/// A convolution function in frequency domain and complex number space
pub trait ComplexFrequencyConvFunction<T>
    where T: RealNumber {
    /// Calculates the convolution for a real data point
    fn calc(&self, x: T) -> Complex<T>;
}

/// Allows to create a lookup table with linear interpolation between table points.
/// This usually speeds up a convolution and sacrifies accuracy.
pub struct RealTimeLinearTableLookup<T>
    where T: RealNumber {
    table: Vec<T>,
    delta: T,
    offset: T
}

/// Allows to create a lookup table with linear interpolation between table points.
/// This usually speeds up a convolution and sacrifies accuracy.
pub struct ComplexTimeLinearTableLookup<T>
    where T: RealNumber {
    table: Vec<Complex<T>>,
    delta: T,
    offset: T
}

/// Allows to create a lookup table with linear interpolation between table points.
/// This usually speeds up a convolution and sacrifies accuracy.
pub struct ComplexFrequencyLinearTableLookup<T>
    where T: RealNumber {
    table: Vec<Complex<T>>,
    delta: T,
    offset: T
}

macro_rules! add_linear_table_lookup_impl {
    ($($name: ident: $conv_type: ident, $($data_type: ident, $result_type:ident),*);*) => {
        $(
            $(
                impl $conv_type<$data_type> for $name<$data_type> {
                    fn calc(&self, x: $data_type) -> $result_type {
                        let len = self.table.len();
                        let center = len / 2;
                        let center_float = center as $data_type;
                        let x = x / self.delta - self.offset + center_float;
                        let round_float = x.round();
                        let round = round_float as usize;
                        if round >= len {
                            return $result_type::zero();
                        }

                        let round_tolerance = 1e-6;
                        if (x - round_float).abs() < round_tolerance {
                            return self.table[round];
                        }

                        if x > round_float {
                            let other = round + 1;
                            if other >= len {
                                return self.table[round];
                            }
                            let y0 = self.table[round];
                            let x0 = round_float;
                            let y1 = self.table[other];
                            return y0 + (y1 - y0) * (x - x0);
                        } else {
                            if round == 0 {
                                return self.table[round];
                            }
                            let other = round - 1;
                            let y0 = self.table[round];
                            let x0 = round_float;
                            let y1 = self.table[other];
                            return y0 + (y1 - y0) * (x0 - x);
                        }
                    }
                }

                impl $name<$data_type> {
                    /// Creates a lookup table by putting the pieces together.
                    pub fn from_raw_parts(table: &[$result_type], delta: $data_type, offset: $data_type) -> Self {
                        let owned_table = Vec::from(table);
                        $name { table: owned_table, delta: delta, offset: offset }
                    }

                    /// Creates a lookup table from another convolution function. The `delta` argument
                    /// can be used to balance performance vs. accuracy.
                    pub fn from_conv_function(other: &$conv_type<$data_type>, delta: $data_type, offset: $data_type, len: usize) -> Self {
                        let center = (len / 2) as isize;
                        let mut table = vec![$result_type::zero(); len];
                        let mut i = -center;
                        for n in &mut table {
                            *n = other.calc((i as $data_type) * delta);
                            i += 1;
                        }
                        $name { table: table, delta: delta, offset: offset }
                    }
                }
            )*
        )*
    }
}
add_linear_table_lookup_impl!(
    RealTimeLinearTableLookup: RealTimeConvFunction, f32, f32, f64, f64;
    ComplexTimeLinearTableLookup: ComplexTimeConvFunction, f32, Complex32, f64, Complex64;
    ComplexFrequencyLinearTableLookup: ComplexFrequencyConvFunction, f32, Complex32, f64, Complex64);

macro_rules! add_real_time_linear_table_impl {
    ($($data_type: ident),*) => {
        $(
            impl RealTimeLinearTableLookup<$data_type> {
                /// Convert the lookup table into complex number space
                pub fn to_complex(&self) -> ComplexTimeLinearTableLookup<$data_type> {
                    let vector = RealTimeVector::from_array(&self.table);
                    let complex = vector.to_complex().expect("to_complex shouldn't fail");
                    let complex = complex.complex_data();
                    let mut table = Vec::with_capacity(complex.len());
                    for n in complex {
                        table.push(*n);
                    }
                    ComplexTimeLinearTableLookup { table: table, delta: self.delta, offset: self.offset }
                }
            }
        )*
    }
}
add_real_time_linear_table_impl!(f32, f64);

macro_rules! add_complex_time_linear_table_impl {
    ($($data_type: ident),*) => {
        $(
            impl ComplexTimeLinearTableLookup<$data_type> {
                /// Convert the lookup table into real number space
                pub fn to_real(self) -> RealTimeLinearTableLookup<$data_type> {
                    let vector = ComplexTimeVector::from_complex(&self.table);
                    let real = vector.to_real().expect("to_complex shouldn't fail");
                    let real = real.data();
                    let mut table = Vec::with_capacity(real.len());
                    for n in real {
                        table.push(*n);
                    }
                    RealTimeLinearTableLookup { table: table, delta: self.delta, offset: self.offset }
                }

                /// Convert the lookup table into frequency domain
                pub fn fft(self) -> ComplexFrequencyLinearTableLookup<$data_type> {
                    let vector = ComplexTimeVector::from_complex(&self.table);
                    let freq = vector.fft().expect("vector fft shouldn't fail");
                    let freq = freq.complex_data();
                    let mut table = Vec::with_capacity(freq.len());
                    for n in freq {
                        table.push(*n);
                    }
                    ComplexFrequencyLinearTableLookup { table: table, delta: self.delta, offset: self.offset }
                }
            }
        )*
    }
}
add_complex_time_linear_table_impl!(f32, f64);

macro_rules! add_complex_frequency_linear_table_impl {
    ($($data_type: ident),*) => {
        $(
            impl ComplexFrequencyLinearTableLookup<$data_type> {

                /// Convert the lookup table into time domain
                pub fn ifft(self) -> ComplexTimeLinearTableLookup<$data_type> {
                    let vector = ComplexFreqVector::from_complex(&self.table);
                    let time = vector.ifft().expect("vector ifft shouldn't fail");
                    let time = time.complex_data();
                    let mut table = Vec::with_capacity(time.len());
                    for n in time {
                        table.push(*n);
                    }
                    ComplexTimeLinearTableLookup { table: table, delta: self.delta, offset: self.offset }
                }
            }
        )*
    }
}
add_complex_frequency_linear_table_impl!(f32, f64);

/// Raised cosine function according to https://en.wikipedia.org/wiki/Raised-cosine_filter
pub struct  RaiseCosineFuncton<T>
    where T: RealNumber {
    rolloff: T
}

impl<T> RealTimeConvFunction<T> for RaiseCosineFuncton<T>
    where T: RealNumber {
    fn calc(&self, x: T) -> T {
        if x == T::zero() {
            return T::one();
        }

        let one = T::one();
        let two = T::from(2.0).unwrap();
        let pi = two * one.asin();
        let four = two * two;
        if x.abs() == one / (two * self.rolloff) {
            let arg = pi / two / self.rolloff;
            return (arg).sin() / arg * pi / four;
        }

        let pi_x = pi * x;
        let arg = two * self.rolloff * x;
        return pi_x.sin() * (pi_x * self.rolloff).cos() / pi_x / (one - (arg * arg))
    }
}

impl<T> RaiseCosineFuncton<T>
    where T: RealNumber {
    /// Creates a root rised cosine function.
    pub fn new(rolloff: T) -> Self {
        RaiseCosineFuncton { rolloff: rolloff }
    }
}

#[cfg(test)]
mod tests {
	use super::*;
    use super::super::RealNumber;
    use std::fmt::Debug;

    fn conv_test<T, C>(conv: C, expected: &[T], step: T, tolerance: T)
        where T: RealNumber + Debug,
              C: RealTimeConvFunction<T> {
        let mut result = vec![T::zero(); expected.len()];
        let mut j = -(expected.len() as isize / 2);
        for i in 0..result.len() {
            result[i] = conv.calc(T::from(j).unwrap() * step);
            j += 1;
        }

        for i in 0..result.len() {
            if (result[i] - expected[i]).abs() > tolerance {
                panic!("assertion failed: {:?} != {:?}", result, expected);
            }
        }
    }

	#[test]
	fn raised_cosine32_test()
	{
        let window = RaiseCosineFuncton::new(0.35);
        let expected =
            [0.0, 0.2171850639713355, 0.4840621929215732, 0.7430526238101408, 0.9312114164253432,
             1.0, 0.9312114164253432, 0.7430526238101408, 0.4840621929215732, 0.2171850639713355];
        conv_test(window, &expected, 0.2, 1e-4);
	}

    #[test]
    fn lookup_table32_test() {
        let window = RaiseCosineFuncton::new(0.35);
        let table = RealTimeLinearTableLookup::<f64>::from_conv_function(&window, 0.2, 0.0, 10);
        let expected =
            [0.0, 0.2171850639713355, 0.4840621929215732, 0.7430526238101408, 0.9312114164253432,
             1.0, 0.9312114164253432, 0.7430526238101408, 0.4840621929215732, 0.2171850639713355];
        conv_test(table, &expected, 0.2, 1e-4);
    }

    #[test]
    fn linear_interpolation_lookup_table32_test() {
        let window = RaiseCosineFuncton::new(0.35);
        let table = RealTimeLinearTableLookup::<f64>::from_conv_function(&window, 0.4, 0.0, 10);
        let expected =
            [0.0, 0.2171850639713355, 0.4840621929215732, 0.7430526238101408, 0.9312114164253432,
             1.0, 0.9312114164253432, 0.7430526238101408, 0.4840621929215732, 0.2171850639713355];
        conv_test(table, &expected, 0.2, 0.1);
    }
}<|endoftext|>


<commit_before>use std::ffi::OsString;
use std::{fs, io, mem, path};

use std::os::unix::fs::MetadataExt;

#[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
use std::io::{Read, Seek, SeekFrom, Write};

#[cfg(all(feature = "mmap", target_pointer_width = "64"))]
use memmap::MmapMut;

#[cfg(all(feature = "mmap", target_pointer_width = "64"))]
use util::io_err;
use util::{native, MHashMap};
use CONFIG;

const PB_LEN: usize = 256;

pub struct BufCache {
    path_a: OsString,
    path_b: OsString,
    buf: Vec<u8>,
}

/// Holds a file and mmap cache. Because 32 bit systems
/// can't mmap large files, we load them as needed.
pub struct FileCache {
    files: MHashMap<path::PathBuf, Entry>,
}

#[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
pub struct Entry {
    used: bool,
    alloc_failed: bool,
    sparse: bool,
    file: fs::File,
}

#[cfg(all(feature = "mmap", target_pointer_width = "64"))]
pub struct Entry {
    mmap: MmapMut,
    used: bool,
    alloc_failed: bool,
    sparse: bool,
}

pub struct TempPB<'a> {
    path: path::PathBuf,
    buf: &'a mut OsString,
}

pub struct TempBuf<'a> {
    buf: &'a mut Vec<u8>,
}

impl<'a> TempBuf<'a> {
    pub fn get(&mut self, len: usize) -> &mut [u8] {
        unsafe {
            self.buf.reserve(len);
            self.buf.set_len(len);
        }
        &mut self.buf[..]
    }
}

fn get_pb(buf: &mut OsString) -> TempPB {
    debug_assert!(buf.capacity() >= PB_LEN);
    let path = mem::replace(buf, OsString::with_capacity(0)).into();
    TempPB { buf, path }
}

impl<'a> TempPB<'a> {
    pub fn get<P: AsRef<path::Path>>(&mut self, base: P) -> &mut path::PathBuf {
        self.clear();
        self.path.push(base.as_ref());
        &mut self.path
    }

    fn clear(&mut self) {
        let mut s =
            mem::replace(&mut self.path, OsString::with_capacity(0).into()).into_os_string();
        s.clear();
        self.path = s.into();
    }
}

impl<'a> Drop for TempPB<'a> {
    fn drop(&mut self) {
        let mut path =
            mem::replace(&mut self.path, OsString::with_capacity(0).into()).into_os_string();
        mem::swap(self.buf, &mut path);
        self.buf.clear();
    }
}

impl BufCache {
    pub fn new() -> BufCache {
        BufCache {
            path_a: OsString::with_capacity(PB_LEN),
            path_b: OsString::with_capacity(PB_LEN),
            buf: Vec::with_capacity(1_048_576),
        }
    }

    pub fn data(&mut self) -> (TempBuf, TempPB, TempPB) {
        (
            TempBuf { buf: &mut self.buf },
            get_pb(&mut self.path_a),
            get_pb(&mut self.path_b),
        )
    }
}

impl FileCache {
    pub fn new() -> FileCache {
        FileCache {
            files: MHashMap::default(),
        }
    }

    pub fn read_file_range(
        &mut self,
        path: &path::Path,
        offset: u64,
        buf: &mut [u8],
    ) -> io::Result<()> {
        self.ensure_exists(path, Err(0))?;

        #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
        {
            let entry = self.files.get_mut(path).unwrap();
            entry.file.seek(SeekFrom::Start(offset))?;
            entry.file.read_exact(buf)?;
            Ok(())
        }

        #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
        {
            let len = buf.len();
            let entry = self.files.get_mut(path).unwrap();
            entry.used = true;
            if entry.sparse {
                let res = native::mmap_read(
                    &entry.mmap[offset as usize..offset as usize + len],
                    buf,
                    len,
                );
                if res.is_err() {
                    return io_err("Disk full!");
                }
            } else {
                buf.copy_from_slice(&entry.mmap[offset as usize..offset as usize + len]);
            };
            Ok(())
        }
    }

    pub fn write_file_range(
        &mut self,
        path: &path::Path,
        size: Result<u64, u64>,
        offset: u64,
        buf: &[u8],
    ) -> io::Result<()> {
        self.ensure_exists(path, size)?;

        #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
        {
            let entry = self.files.get_mut(path).unwrap();
            entry.file.seek(SeekFrom::Start(offset))?;
            entry.file.write_all(&buf)?;
            Ok(())
        }

        #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
        {
            let len = buf.len();
            let entry = self.files.get_mut(path).unwrap();
            entry.used = true;
            if entry.sparse {
                let res = native::mmap_write(
                    &mut entry.mmap[offset as usize..offset as usize + len],
                    &buf,
                    len,
                );
                if res.is_err() {
                    return io_err("Disk full!");
                }
            } else {
                (&mut entry.mmap[offset as usize..offset as usize + len]).copy_from_slice(&buf);
            };
            Ok(())
        }
    }

    pub fn remove_file(&mut self, path: &path::Path) {
        #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
        self.files.remove(path);
        #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
        self.files.remove(path).map(|f| f.mmap.flush_async().ok());
    }

    pub fn flush_file(&mut self, path: &path::Path) {
        #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
        {
            self.files.get_mut(path).map(|e| e.file.sync_all().ok());
        }
        #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
        {
            self.files.get_mut(path).map(|f| f.mmap.flush_async().ok());
        }
    }

    fn ensure_exists(&mut self, path: &path::Path, len: Result<u64, u64>) -> io::Result<()> {
        let len_val = if let Ok(v) = len {
            v
        } else {
            len.err().unwrap()
        };
        if !self.files.contains_key(path) {
            if self.files.len() >= CONFIG.net.max_open_files {
                let mut removal = None;
                // We rely on random iteration order to prove us something close to a "clock hand"
                // like algorithm
                for (id, entry) in &mut self.files {
                    if entry.used {
                        entry.used = false;
                    } else {
                        removal = Some(id.clone());
                    }
                }
                if let Some(f) = removal {
                    self.remove_file(&f);
                }
            }

            fs::create_dir_all(path.parent().unwrap())?;
            let file = fs::OpenOptions::new()
                .write(true)
                .create(true)
                .read(true)
                .open(path)?;

            let alloc_failed = if len.is_ok() && file.metadata()?.len() != len.ok().unwrap() {
                let res = !native::fallocate(&file, len.unwrap())?;
                debug!("Attempted to fallocate {:?}: success {}!", path, !res);
                res
            } else {
                if len_val != 0 {
                    file.set_len(len_val)?;
                }
                false
            };

            let stat = file.metadata()?;
            let sparse = stat.blocks() * stat.blksize() < stat.size();

            #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
            self.files.insert(
                path.to_path_buf(),
                Entry {
                    file,
                    used: true,
                    sparse,
                    alloc_failed,
                },
            );

            #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
            {
                // Check if the file was never allocated
                if stat.size() == 0 {
                    return io_err("mmap attempted on 0 sized file");
                }
                let mmap = unsafe { MmapMut::map_mut(&file)? };
                self.files.insert(
                    path.to_path_buf(),
                    Entry {
                        mmap,
                        sparse,
                        used: true,
                        alloc_failed,
                    },
                );
            }
        } else if len.is_ok() {
            let entry = self.files.get_mut(path).unwrap();
            if entry.sparse && !entry.alloc_failed {
                debug!("Attempting delayed falloc!");
                let file = fs::OpenOptions::new().write(true).read(true).open(path)?;
                entry.alloc_failed = !native::fallocate(&file, len_val)?;
                if !entry.alloc_failed {
                    entry.sparse = false;
                }
            }
        }
        Ok(())
    }
}

impl Drop for FileCache {
    fn drop(&mut self) {
        #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
        {
            for (_, entry) in self.files.drain() {
                entry.file.sync_all().ok();
            }
        }
        #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
        {
            for (_, entry) in self.files.drain() {
                entry.mmap.flush().ok();
            }
        }
    }
}
<commit_msg>Remove unsafe temp buffer resizing code<commit_after>use std::ffi::OsString;
use std::{fs, io, mem, path};

use std::os::unix::fs::MetadataExt;

#[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
use std::io::{Read, Seek, SeekFrom, Write};

#[cfg(all(feature = "mmap", target_pointer_width = "64"))]
use memmap::MmapMut;

#[cfg(all(feature = "mmap", target_pointer_width = "64"))]
use util::io_err;
use util::{native, MHashMap};
use CONFIG;

const PB_LEN: usize = 256;

pub struct BufCache {
    path_a: OsString,
    path_b: OsString,
    buf: Vec<u8>,
}

/// Holds a file and mmap cache. Because 32 bit systems
/// can't mmap large files, we load them as needed.
pub struct FileCache {
    files: MHashMap<path::PathBuf, Entry>,
}

#[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
pub struct Entry {
    used: bool,
    alloc_failed: bool,
    sparse: bool,
    file: fs::File,
}

#[cfg(all(feature = "mmap", target_pointer_width = "64"))]
pub struct Entry {
    mmap: MmapMut,
    used: bool,
    alloc_failed: bool,
    sparse: bool,
}

pub struct TempPB<'a> {
    path: path::PathBuf,
    buf: &'a mut OsString,
}

pub struct TempBuf<'a> {
    buf: &'a mut Vec<u8>,
}

impl<'a> TempBuf<'a> {
    pub fn get(&mut self, len: usize) -> &mut [u8] {
        self.buf.reserve(len);
        if self.buf.len() < len {
            self.buf.resize(len, 0u8);
        }
        &mut self.buf[..len]
    }
}

fn get_pb(buf: &mut OsString) -> TempPB {
    debug_assert!(buf.capacity() >= PB_LEN);
    let path = mem::replace(buf, OsString::with_capacity(0)).into();
    TempPB { buf, path }
}

impl<'a> TempPB<'a> {
    pub fn get<P: AsRef<path::Path>>(&mut self, base: P) -> &mut path::PathBuf {
        self.clear();
        self.path.push(base.as_ref());
        &mut self.path
    }

    fn clear(&mut self) {
        let mut s =
            mem::replace(&mut self.path, OsString::with_capacity(0).into()).into_os_string();
        s.clear();
        self.path = s.into();
    }
}

impl<'a> Drop for TempPB<'a> {
    fn drop(&mut self) {
        let mut path =
            mem::replace(&mut self.path, OsString::with_capacity(0).into()).into_os_string();
        mem::swap(self.buf, &mut path);
        self.buf.clear();
    }
}

impl BufCache {
    pub fn new() -> BufCache {
        BufCache {
            path_a: OsString::with_capacity(PB_LEN),
            path_b: OsString::with_capacity(PB_LEN),
            buf: Vec::with_capacity(1_048_576),
        }
    }

    pub fn data(&mut self) -> (TempBuf, TempPB, TempPB) {
        (
            TempBuf { buf: &mut self.buf },
            get_pb(&mut self.path_a),
            get_pb(&mut self.path_b),
        )
    }
}

impl FileCache {
    pub fn new() -> FileCache {
        FileCache {
            files: MHashMap::default(),
        }
    }

    pub fn read_file_range(
        &mut self,
        path: &path::Path,
        offset: u64,
        buf: &mut [u8],
    ) -> io::Result<()> {
        self.ensure_exists(path, Err(0))?;

        #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
        {
            let entry = self.files.get_mut(path).unwrap();
            entry.file.seek(SeekFrom::Start(offset))?;
            entry.file.read_exact(buf)?;
            Ok(())
        }

        #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
        {
            let len = buf.len();
            let entry = self.files.get_mut(path).unwrap();
            entry.used = true;
            if entry.sparse {
                let res = native::mmap_read(
                    &entry.mmap[offset as usize..offset as usize + len],
                    buf,
                    len,
                );
                if res.is_err() {
                    return io_err("Disk full!");
                }
            } else {
                buf.copy_from_slice(&entry.mmap[offset as usize..offset as usize + len]);
            };
            Ok(())
        }
    }

    pub fn write_file_range(
        &mut self,
        path: &path::Path,
        size: Result<u64, u64>,
        offset: u64,
        buf: &[u8],
    ) -> io::Result<()> {
        self.ensure_exists(path, size)?;

        #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
        {
            let entry = self.files.get_mut(path).unwrap();
            entry.file.seek(SeekFrom::Start(offset))?;
            entry.file.write_all(&buf)?;
            Ok(())
        }

        #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
        {
            let len = buf.len();
            let entry = self.files.get_mut(path).unwrap();
            entry.used = true;
            if entry.sparse {
                let res = native::mmap_write(
                    &mut entry.mmap[offset as usize..offset as usize + len],
                    &buf,
                    len,
                );
                if res.is_err() {
                    return io_err("Disk full!");
                }
            } else {
                (&mut entry.mmap[offset as usize..offset as usize + len]).copy_from_slice(&buf);
            };
            Ok(())
        }
    }

    pub fn remove_file(&mut self, path: &path::Path) {
        #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
        self.files.remove(path);
        #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
        self.files.remove(path).map(|f| f.mmap.flush_async().ok());
    }

    pub fn flush_file(&mut self, path: &path::Path) {
        #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
        {
            self.files.get_mut(path).map(|e| e.file.sync_all().ok());
        }
        #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
        {
            self.files.get_mut(path).map(|f| f.mmap.flush_async().ok());
        }
    }

    fn ensure_exists(&mut self, path: &path::Path, len: Result<u64, u64>) -> io::Result<()> {
        let len_val = if let Ok(v) = len {
            v
        } else {
            len.err().unwrap()
        };
        if !self.files.contains_key(path) {
            if self.files.len() >= CONFIG.net.max_open_files {
                let mut removal = None;
                // We rely on random iteration order to prove us something close to a "clock hand"
                // like algorithm
                for (id, entry) in &mut self.files {
                    if entry.used {
                        entry.used = false;
                    } else {
                        removal = Some(id.clone());
                    }
                }
                if let Some(f) = removal {
                    self.remove_file(&f);
                }
            }

            fs::create_dir_all(path.parent().unwrap())?;
            let file = fs::OpenOptions::new()
                .write(true)
                .create(true)
                .read(true)
                .open(path)?;

            let alloc_failed = if len.is_ok() && file.metadata()?.len() != len.ok().unwrap() {
                let res = !native::fallocate(&file, len.unwrap())?;
                debug!("Attempted to fallocate {:?}: success {}!", path, !res);
                res
            } else {
                if len_val != 0 {
                    file.set_len(len_val)?;
                }
                false
            };

            let stat = file.metadata()?;
            let sparse = stat.blocks() * stat.blksize() < stat.size();

            #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
            self.files.insert(
                path.to_path_buf(),
                Entry {
                    file,
                    used: true,
                    sparse,
                    alloc_failed,
                },
            );

            #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
            {
                // Check if the file was never allocated
                if stat.size() == 0 {
                    return io_err("mmap attempted on 0 sized file");
                }
                let mmap = unsafe { MmapMut::map_mut(&file)? };
                self.files.insert(
                    path.to_path_buf(),
                    Entry {
                        mmap,
                        sparse,
                        used: true,
                        alloc_failed,
                    },
                );
            }
        } else if len.is_ok() {
            let entry = self.files.get_mut(path).unwrap();
            if entry.sparse && !entry.alloc_failed {
                debug!("Attempting delayed falloc!");
                let file = fs::OpenOptions::new().write(true).read(true).open(path)?;
                entry.alloc_failed = !native::fallocate(&file, len_val)?;
                if !entry.alloc_failed {
                    entry.sparse = false;
                }
            }
        }
        Ok(())
    }
}

impl Drop for FileCache {
    fn drop(&mut self) {
        #[cfg(any(target_pointer_width = "32", not(feature = "mmap")))]
        {
            for (_, entry) in self.files.drain() {
                entry.file.sync_all().ok();
            }
        }
        #[cfg(all(feature = "mmap", target_pointer_width = "64"))]
        {
            for (_, entry) in self.files.drain() {
                entry.mmap.flush().ok();
            }
        }
    }
}

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

    #[test]
    fn test_tempbuf() {
        let mut data = vec![];
        let mut buf = TempBuf { buf: &mut data };
        assert_eq!(buf.get(10).len(), 10);
        assert_eq!(buf.get(20).len(), 20);
        assert_eq!(buf.get(10).len(), 10);
        assert_eq!(buf.get(30).len(), 30);
        assert_eq!(buf.get(10).len(), 10);
    }
}
<|endoftext|>


<commit_before>//! Facilities for implementing chess engines.

pub mod time_manager;

use std::collections::VecDeque;
use std::ops::Deref;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use std::cmp::min;
use std::io;
use uci::*;
use search::*;
use self::time_manager::*;

/// Serves UCI commands until a "quit" command is received.
///
/// The current thread will block until the UCI session is closed.
///
/// Returns `Err` if the handshake was unsuccessful, or if an IO error
/// occurred.
pub fn run_server<S: SearchExecutor<ReportData = Vec<Variation>>>() -> io::Result<()> {
    run_engine::<Engine<S>>()
}

/// The chess starting position in Forsyth–Edwards notation (FEN).
const START_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

/// The version of the program.
const VERSION: &'static str = "0.1";

/// The name of the program.
const NAME: &'static str = "Alcibiades";

/// The author of the program.
const AUTHOR: &'static str = "Evgeni Pandurski";

/// Represents a condition for terminating the search.
enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(u64), // Stop when the given number of nodes has been searched.
    Depth(Depth), // Stop when the given search depth has been completed.
    Never, // An infinite search.
}

/// Implements `UciEngine` trait.
struct Engine<S: SearchExecutor<ReportData = Vec<Variation>>> {
    tt: Arc<S::HashTable>,
    position: S::SearchNode,
    searcher: S,

    // The starting time of the current/last search.
    started_at: SystemTime,

    // The status of the current/last search.
    status: SearchStatus,

    // Tells the engine when it must stop thinking and play the best
    // move it has found.
    play_when: PlayWhen,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,

    // `true` if the engine is thinking in pondering mode at the moment.
    is_pondering: bool,

    // A queue for the messages send by the engine to the GUI.
    queue: VecDeque<EngineReply>,

    // Helps the engine decide when to send periodic progress reports.
    silent_since: SystemTime,
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> UciEngine for Engine<S> {
    fn name() -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author() -> String {
        AUTHOR.to_string()
    }

    fn options() -> Vec<(String, OptionDescription)> {
        // Add up all suported options.
        let mut options = vec![
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 64 * 1024, default: 16 }),
            ("Clear Hash".to_string(), OptionDescription::Button),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ];
        options.extend(S::options());

        // Remove duplicated options.
        options.sort_by(|a, b| a.0.cmp(&b.0));
        let mut options_dedup = vec![];
        let mut prev_name = String::from("");
        for o in options.drain(..) {
            if o.0 == prev_name {
                continue;
            }
            prev_name = o.0.clone();
            options_dedup.push(o);
        }
        options_dedup
    }

    fn new(tt_size_mb: Option<usize>) -> Engine<S> {
        let tt = Arc::new(S::HashTable::new(tt_size_mb));
        Engine {
            tt: tt.clone(),
            position: S::SearchNode::from_history(START_FEN, &mut vec![].into_iter()).ok().unwrap(),
            searcher: S::new(tt),
            started_at: SystemTime::now(),
            status: SearchStatus {
                done: true,
                depth: 0,
                searched_nodes: 0,
                duration_millis: 0,
                nps: 0,
            },
            play_when: PlayWhen::Never,
            pondering_is_allowed: false,
            is_pondering: false,
            queue: VecDeque::new(),
            silent_since: SystemTime::now(),
        }
    }

    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                // TODO: Can we remove this?
                self.pondering_is_allowed = value == "true";
            }
            "Hash" => {
                // We do not support re-sizing of the transposition
                // table once the engine has been started.
                return;
            }
            "Clear Hash" => {
                self.tt.clear();
            }
            _ => (),
        }
        S::set_option(name, value);
    }

    fn new_game(&mut self) {
        self.tt.clear();
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if let Ok(p) = S::SearchNode::from_history(fen, moves) {
            self.position = p;
        }
    }

    fn go(&mut self, params: GoParams) {
        // Note: We ignore the "mate" field.
        //
        // TODO: What should we do with "mate"?
        let GoParams { searchmoves,
                       ponder,
                       wtime,
                       btime,
                       winc,
                       binc,
                       movestogo,
                       depth,
                       nodes,
                       movetime,
                       infinite,
                       .. } = params;

        self.terminate();
        self.tt.new_search();
        self.is_pondering = ponder;
        self.play_when = if infinite {
            PlayWhen::Never
        } else if movetime.is_some() {
            PlayWhen::MoveTime(movetime.unwrap())
        } else if nodes.is_some() {
            PlayWhen::Nodes(nodes.unwrap())
        } else if depth.is_some() {
            PlayWhen::Depth(min(depth.unwrap(), DEPTH_MAX as u64) as Depth)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(self.position.board().to_move,
                                                      self.pondering_is_allowed,
                                                      wtime,
                                                      btime,
                                                      winc,
                                                      binc,
                                                      movestogo))
        };
        self.silent_since = SystemTime::now();
        self.start(searchmoves);
    }

    fn ponder_hit(&mut self) {
        if self.status.done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.terminate();
        self.queue_best_move();
    }

    fn wait_for_reply(&mut self, duration: Duration) -> Option<EngineReply> {
        if self.queue.is_empty() {
            let done = self.status.done;

            // Wait for `search_thread` to do some work, and hopefully
            // update its status. (We must do this even when the
            // search is done -- in that case the next line will just
            // yield the CPU to another process.)
            self.wait_status_update(duration);

            if !done {
                let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } =
                    &self.status;

                // Check if we must play now.
                if !self.is_pondering &&
                   match self.play_when {
                    PlayWhen::TimeManagement(ref tm) => done || tm.must_play(),
                    PlayWhen::MoveTime(t) => done || duration_millis >= t,
                    PlayWhen::Nodes(n) => done || searched_nodes >= n,
                    PlayWhen::Depth(d) => done || depth >= d,
                    PlayWhen::Never => false,
                } {
                    self.stop();
                }
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.terminate();
    }
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> Engine<S> {
    /// Adds a progress report message to the queue.
    fn queue_progress_report(&mut self) {
        let SearchStatus { ref depth, ref searched_nodes, ref nps, .. } = self.status;
        self.queue.push_back(EngineReply::Info(vec![
            InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
            InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
            InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
        ]));
        self.silent_since = SystemTime::now();
    }

    /// Adds a message containing the current (multi)PV to the queue.
    fn queue_variations(&mut self, variations: &Vec<Variation>) {
        let SearchStatus { ref depth, ref searched_nodes, ref duration_millis, ref nps, .. } =
            self.status;
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut moves_string = String::new();
            for m in moves {
                moves_string.push_str(&m.notation());
                moves_string.push(' ');
            }
            self.queue.push_back(EngineReply::Info(vec![
                InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
                InfoItem { info_type: "multipv".to_string(), data: format!("{}", i + 1) },
                InfoItem { info_type: "score".to_string(), data: format!("cp {}{}", value, bound_suffix) },
                InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
                InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
                InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
                InfoItem { info_type: "pv".to_string(), data: moves_string },
            ]));
        }
        self.silent_since = SystemTime::now();
    }

    /// Adds a message containing the current best move to the queue.
    fn queue_best_move(&mut self) {
        let pv = extract_pv(self.tt.deref(), &self.position, 2);
        let best_move = if let Some(m) = pv.moves.get(0) {
            m.notation()
        } else {
            // If the PV is empty, pick the first legal move.
            self.position.legal_moves().get(0).map_or("0000".to_string(), |m| m.notation())
        };
        self.queue.push_back(EngineReply::BestMove {
            best_move: best_move,
            ponder_move: pv.moves.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }

    /// Terminates the currently running search (if any), starts a new
    /// search, updates the status.
    fn start(&mut self, mut searchmoves: Vec<String>) {
        // Validate `searchmoves`.
        let mut moves = vec![];
        let legal_moves = self.position.legal_moves();
        if !searchmoves.is_empty() {
            searchmoves.sort();
            for m in legal_moves.iter() {
                if searchmoves.binary_search(&m.notation()).is_ok() {
                    moves.push(*m);
                }
            }
        };
        let searchmoves = if moves.is_empty() {
            legal_moves
        } else {
            moves
        };

        // Terminate the currently running search.
        self.terminate();

        // Update the status.
        self.status = SearchStatus {
            done: false,
            depth: 0,
            searched_nodes: 0,
            duration_millis: 0,
            ..self.status
        };

        // Start a new search.
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: self.position.clone(),
            depth: DEPTH_MAX,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: searchmoves,
        });
    }

    /// Terminates the currently running search (if any), updates the
    /// status.
    fn terminate(&mut self) {
        self.searcher.terminate_search();
        while !self.status.done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    /// Waits for a search status update, timing out after a specified
    /// duration or earlier.
    fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    /// A helper method. It updates the current status according to
    /// the received report message.
    fn process_report(&mut self, report: SearchReport<Vec<Variation>>) {
        // Register the search status with the time manager.
        if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
            tm.update_status(&report);
        }

        let duration = self.started_at.elapsed().unwrap();
        self.status.duration_millis = 1000 * duration.as_secs() +
                                      (duration.subsec_nanos() / 1000000) as u64;
        self.status.searched_nodes = report.searched_nodes;
        self.status.nps = 1000 * (self.status.nps + self.status.searched_nodes) /
                          (1000 + self.status.duration_millis);
        self.status.depth = report.depth;
        self.status.done = report.done;

        // Send the (multi)PV if available.
        self.queue_variations(&report.data);

        // Send a progress report periodically.
        if self.silent_since.elapsed().unwrap().as_secs() > 10 {
            self.queue_progress_report();
        }
    }
}

/// Contains information about the current progress of a search.
struct SearchStatus {
    pub done: bool,
    pub depth: Depth,
    pub searched_nodes: u64,

    /// The duration of the search in milliseconds.
    pub duration_millis: u64,

    /// Average number of analyzed nodes per second.
    pub nps: u64,
}
<commit_msg>fix a regression, not sending periodic progress infos<commit_after>//! Facilities for implementing chess engines.

pub mod time_manager;

use std::collections::VecDeque;
use std::ops::Deref;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use std::cmp::min;
use std::io;
use uci::*;
use search::*;
use self::time_manager::*;

/// Serves UCI commands until a "quit" command is received.
///
/// The current thread will block until the UCI session is closed.
///
/// Returns `Err` if the handshake was unsuccessful, or if an IO error
/// occurred.
pub fn run_server<S: SearchExecutor<ReportData = Vec<Variation>>>() -> io::Result<()> {
    run_engine::<Engine<S>>()
}

/// The chess starting position in Forsyth–Edwards notation (FEN).
const START_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

/// The version of the program.
const VERSION: &'static str = "0.1";

/// The name of the program.
const NAME: &'static str = "Alcibiades";

/// The author of the program.
const AUTHOR: &'static str = "Evgeni Pandurski";

/// Represents a condition for terminating the search.
enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(u64), // Stop when the given number of nodes has been searched.
    Depth(Depth), // Stop when the given search depth has been completed.
    Never, // An infinite search.
}

/// Implements `UciEngine` trait.
struct Engine<S: SearchExecutor<ReportData = Vec<Variation>>> {
    tt: Arc<S::HashTable>,
    position: S::SearchNode,
    searcher: S,

    // The starting time of the current/last search.
    started_at: SystemTime,

    // The status of the current/last search.
    status: SearchStatus,

    // Tells the engine when it must stop thinking and play the best
    // move it has found.
    play_when: PlayWhen,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,

    // `true` if the engine is thinking in pondering mode at the moment.
    is_pondering: bool,

    // A queue for the messages send by the engine to the GUI.
    queue: VecDeque<EngineReply>,

    // Helps the engine decide when to send periodic progress reports.
    silent_since: SystemTime,
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> UciEngine for Engine<S> {
    fn name() -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author() -> String {
        AUTHOR.to_string()
    }

    fn options() -> Vec<(String, OptionDescription)> {
        // Add up all suported options.
        let mut options = vec![
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 64 * 1024, default: 16 }),
            ("Clear Hash".to_string(), OptionDescription::Button),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ];
        options.extend(S::options());

        // Remove duplicated options.
        options.sort_by(|a, b| a.0.cmp(&b.0));
        let mut options_dedup = vec![];
        let mut prev_name = String::from("");
        for o in options.drain(..) {
            if o.0 == prev_name {
                continue;
            }
            prev_name = o.0.clone();
            options_dedup.push(o);
        }
        options_dedup
    }

    fn new(tt_size_mb: Option<usize>) -> Engine<S> {
        let tt = Arc::new(S::HashTable::new(tt_size_mb));
        Engine {
            tt: tt.clone(),
            position: S::SearchNode::from_history(START_FEN, &mut vec![].into_iter()).ok().unwrap(),
            searcher: S::new(tt),
            started_at: SystemTime::now(),
            status: SearchStatus {
                done: true,
                depth: 0,
                searched_nodes: 0,
                duration_millis: 0,
                nps: 0,
            },
            play_when: PlayWhen::Never,
            pondering_is_allowed: false,
            is_pondering: false,
            queue: VecDeque::new(),
            silent_since: SystemTime::now(),
        }
    }

    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                // TODO: Can we remove this?
                self.pondering_is_allowed = value == "true";
            }
            "Hash" => {
                // We do not support re-sizing of the transposition
                // table once the engine has been started.
                return;
            }
            "Clear Hash" => {
                self.tt.clear();
            }
            _ => (),
        }
        S::set_option(name, value);
    }

    fn new_game(&mut self) {
        self.tt.clear();
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if let Ok(p) = S::SearchNode::from_history(fen, moves) {
            self.position = p;
        }
    }

    fn go(&mut self, params: GoParams) {
        // Note: We ignore the "mate" field.
        //
        // TODO: What should we do with "mate"?
        let GoParams { searchmoves,
                       ponder,
                       wtime,
                       btime,
                       winc,
                       binc,
                       movestogo,
                       depth,
                       nodes,
                       movetime,
                       infinite,
                       .. } = params;

        self.terminate();
        self.tt.new_search();
        self.is_pondering = ponder;
        self.play_when = if infinite {
            PlayWhen::Never
        } else if movetime.is_some() {
            PlayWhen::MoveTime(movetime.unwrap())
        } else if nodes.is_some() {
            PlayWhen::Nodes(nodes.unwrap())
        } else if depth.is_some() {
            PlayWhen::Depth(min(depth.unwrap(), DEPTH_MAX as u64) as Depth)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(self.position.board().to_move,
                                                      self.pondering_is_allowed,
                                                      wtime,
                                                      btime,
                                                      winc,
                                                      binc,
                                                      movestogo))
        };
        self.silent_since = SystemTime::now();
        self.start(searchmoves);
    }

    fn ponder_hit(&mut self) {
        if self.status.done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.terminate();
        self.queue_best_move();
    }

    fn wait_for_reply(&mut self, duration: Duration) -> Option<EngineReply> {
        if self.queue.is_empty() {
            let done = self.status.done;

            // Wait for `search_thread` to do some work, and hopefully
            // update its status. (We must do this even when the
            // search is done -- in that case the next line will just
            // yield the CPU to another process.)
            self.wait_status_update(duration);

            if !done {
                let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } =
                    &self.status;

                // Check if we must play now.
                if !self.is_pondering &&
                   match self.play_when {
                    PlayWhen::TimeManagement(ref tm) => done || tm.must_play(),
                    PlayWhen::MoveTime(t) => done || duration_millis >= t,
                    PlayWhen::Nodes(n) => done || searched_nodes >= n,
                    PlayWhen::Depth(d) => done || depth >= d,
                    PlayWhen::Never => false,
                } {
                    self.stop();
                }
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.terminate();
    }
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> Engine<S> {
    /// Adds a progress report message to the queue.
    fn queue_progress_report(&mut self) {
        let SearchStatus { ref depth, ref searched_nodes, ref nps, .. } = self.status;
        self.queue.push_back(EngineReply::Info(vec![
            InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
            InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
            InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
        ]));
        self.silent_since = SystemTime::now();
    }

    /// Adds a message containing the current (multi)PV to the queue.
    fn queue_variations(&mut self, variations: &Vec<Variation>) {
        let SearchStatus { ref depth, ref searched_nodes, ref duration_millis, ref nps, .. } =
            self.status;
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut moves_string = String::new();
            for m in moves {
                moves_string.push_str(&m.notation());
                moves_string.push(' ');
            }
            self.queue.push_back(EngineReply::Info(vec![
                InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
                InfoItem { info_type: "multipv".to_string(), data: format!("{}", i + 1) },
                InfoItem { info_type: "score".to_string(), data: format!("cp {}{}", value, bound_suffix) },
                InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
                InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
                InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
                InfoItem { info_type: "pv".to_string(), data: moves_string },
            ]));
        }
        self.silent_since = SystemTime::now();
    }

    /// Adds a message containing the current best move to the queue.
    fn queue_best_move(&mut self) {
        let pv = extract_pv(self.tt.deref(), &self.position, 2);
        let best_move = if let Some(m) = pv.moves.get(0) {
            m.notation()
        } else {
            // If the PV is empty, pick the first legal move.
            self.position.legal_moves().get(0).map_or("0000".to_string(), |m| m.notation())
        };
        self.queue.push_back(EngineReply::BestMove {
            best_move: best_move,
            ponder_move: pv.moves.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }

    /// Terminates the currently running search (if any), starts a new
    /// search, updates the status.
    fn start(&mut self, mut searchmoves: Vec<String>) {
        // Validate `searchmoves`.
        let mut moves = vec![];
        let legal_moves = self.position.legal_moves();
        if !searchmoves.is_empty() {
            searchmoves.sort();
            for m in legal_moves.iter() {
                if searchmoves.binary_search(&m.notation()).is_ok() {
                    moves.push(*m);
                }
            }
        };
        let searchmoves = if moves.is_empty() {
            legal_moves
        } else {
            moves
        };

        // Terminate the currently running search.
        self.terminate();

        // Update the status.
        self.status = SearchStatus {
            done: false,
            depth: 0,
            searched_nodes: 0,
            duration_millis: 0,
            ..self.status
        };

        // Start a new search.
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: self.position.clone(),
            depth: DEPTH_MAX,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: searchmoves,
        });
    }

    /// Terminates the currently running search (if any), updates the
    /// status.
    fn terminate(&mut self) {
        self.searcher.terminate_search();
        while !self.status.done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    /// Waits for a search status update, timing out after a specified
    /// duration or earlier.
    fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    /// A helper method. It updates the current status according to
    /// the received report message.
    fn process_report(&mut self, report: SearchReport<Vec<Variation>>) {
        // Register the search status with the time manager.
        if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
            tm.update_status(&report);
        }

        let duration = self.started_at.elapsed().unwrap();
        self.status.duration_millis = 1000 * duration.as_secs() +
                                      (duration.subsec_nanos() / 1000000) as u64;
        self.status.searched_nodes = report.searched_nodes;
        self.status.nps = 1000 * (self.status.nps + self.status.searched_nodes) /
                          (1000 + self.status.duration_millis);
        self.status.depth = report.depth;
        self.status.done = report.done;

        // Send the (multi)PV if available.
        if !report.data.is_empty() {
            self.queue_variations(&report.data);
        }

        // Send a progress report periodically.
        if self.silent_since.elapsed().unwrap().as_secs() > 10 {
            self.queue_progress_report();
        }
    }
}

/// Contains information about the current progress of a search.
struct SearchStatus {
    pub done: bool,
    pub depth: Depth,
    pub searched_nodes: u64,

    /// The duration of the search in milliseconds.
    pub duration_millis: u64,

    /// Average number of analyzed nodes per second.
    pub nps: u64,
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Factors_of_an_integer
use std::num;

#[cfg(not(test))]
fn main() {
    let target = 78i;
    println!("Factors of integer {}:", target);
    let factors = factor_int(target);

    for f in factors.iter() {
        println!("{}", f);
    }
}

// Compute the factors of an integer
// This method uses a simple check on each value between 1 and sqrt(x) to find
// pairs of factors
fn factor_int(x: int) -> Box<Vec<int>> {
    let mut factors: Vec<int> = Vec::new();

    // Compute the largest value to check i.e. sqrt(x)
    // Since we expect sane values rather than user input, we just fail if the
    // type conversions don't work
    let x_f: f64 = num::from_int(x).expect("Math fail!");
    let bound_f = x_f.sqrt();
    let bound: int = num::from_f64(bound_f.floor()).expect("Math fail!");

    for i in range(1i, bound) {
        if x % i == 0 {
            factors.push(i);
            factors.push(x/i);
        }
    }

    box factors
}

#[test]
fn test() {
    let result = factor_int(78i);
    assert_eq!(*result, vec![1i, 78, 2, 39, 3, 26, 6, 13]);
}
<commit_msg>Code cleanup based on aochagavia's suggestions<commit_after>// Implements http://rosettacode.org/wiki/Factors_of_an_integer

#[cfg(not(test))]
fn main() {
    let target = 78i;
    println!("Factors of integer {}:", target);
    let factors = factor_int(target);

    for f in factors.iter() {
        println!("{}", f);
    }
}

// Compute the factors of an integer
// This method uses a simple check on each value between 1 and sqrt(x) to find
// pairs of factors
fn factor_int(x: int) -> Vec<int> {
    let mut factors: Vec<int> = Vec::new();

    let bound: int = (x as f64).sqrt().floor() as int;

    for i in range(1i, bound) {
        if x % i == 0 {
            factors.push(i);
            factors.push(x/i);
        }
    }

    factors
}

#[test]
fn test() {
    let result = factor_int(78i);
    assert_eq!(result, vec![1i, 78, 2, 39, 3, 26, 6, 13]);
}
<|endoftext|>