<commit_before>use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

use crate::stacked_borrows::Tag;
use crate::*;

struct FileHandle {
    file: File,
    flag: i32,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        if flag != this.eval_libc_i32("O_RDONLY")? && flag != this.eval_libc_i32("O_CLOEXEC")? {
            throw_unsup_format!("Unsupported flag {:#x}", flag);
        }

        let path_bytes = this
            .memory()
            .read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
        let path = std::str::from_utf8(path_bytes)
            .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;

        match File::open(path) {
            Ok(file) => {
                let mut fh = &mut this.machine.file_handler;
                fh.low += 1;
                fh.handles.insert(fh.low, FileHandle{ file, flag});
                Ok(fh.low)
            }

            Err(e) => {
                this.machine.last_error = e.raw_os_error().unwrap() as u32;
                Ok(-1)
            }
        }
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        arg_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;

        if cmd == this.eval_libc_i32("F_SETFD")? {
            // This does not affect the file itself. Certain flags might require changing the file
            // or the way it is accessed somehow.
            let flag = this.read_scalar(arg_op.unwrap())?.to_i32()?;
            // The only usage of this in stdlib at the moment is to enable the `FD_CLOEXEC` flag.
            let fd_cloexec = this.eval_libc_i32("FD_CLOEXEC")?;
            if let Some(FileHandle{ flag: old_flag, .. }) = this.machine.file_handler.handles.get_mut(&fd) {
                if flag ^ *old_flag == fd_cloexec {
                    *old_flag = flag;
                } else {
                    throw_unsup_format!("Unsupported arg {:#x} for `F_SETFD`", flag);
                }
            }
            Ok(0)
        } else if cmd == this.eval_libc_i32("F_GETFD")? {
            if let Some(handle) = this.machine.file_handler.handles.get(&fd) {
                Ok(handle.flag)
            } else {
                this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
                Ok(-1)
            }
        } else {
            throw_unsup_format!("Unsupported command {:#x}", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            match handle.file.sync_all() {
                Ok(()) => Ok(0),
                Err(e) => {
                    this.machine.last_error = e.raw_os_error().unwrap() as u32;
                    Ok(-1)
                }
            }
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            Ok(-1)
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let tcx = &{ this.tcx.tcx };

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;
        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;

        if let Some(FileHandle { file, ..}) = this.machine.file_handler.handles.get_mut(&fd) {
            let mut bytes = vec![0; count as usize];
            match file.read(&mut bytes) {
                Ok(read_bytes) => {
                    bytes.truncate(read_bytes);

                    this.memory_mut()
                        .get_mut(buf.alloc_id)?
                        .write_bytes(tcx, buf, &bytes)?;

                    Ok(read_bytes as i64)
                }
                Err(e) => {
                    this.machine.last_error = e.raw_os_error().unwrap() as u32;
                    Ok(-1)
                }
            }
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            Ok(-1)
        }
    }
}
<commit_msg>Add method to consume std::io::Result<commit_after>use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

use crate::stacked_borrows::Tag;
use crate::*;

struct FileHandle {
    file: File,
    flag: i32,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        if flag != this.eval_libc_i32("O_RDONLY")? && flag != this.eval_libc_i32("O_CLOEXEC")? {
            throw_unsup_format!("Unsupported flag {:#x}", flag);
        }

        let path_bytes = this
            .memory()
            .read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
        let path = std::str::from_utf8(path_bytes)
            .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?
            .to_owned();
        let fd = File::open(&path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle{ file, flag});
            fh.low
        });

        this.consume_result::<i32>(fd, -1)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        arg_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;

        if cmd == this.eval_libc_i32("F_SETFD")? {
            // This does not affect the file itself. Certain flags might require changing the file
            // or the way it is accessed somehow.
            let flag = this.read_scalar(arg_op.unwrap())?.to_i32()?;
            // The only usage of this in stdlib at the moment is to enable the `FD_CLOEXEC` flag.
            let fd_cloexec = this.eval_libc_i32("FD_CLOEXEC")?;
            if let Some(FileHandle{ flag: old_flag, .. }) = this.machine.file_handler.handles.get_mut(&fd) {
                if flag ^ *old_flag == fd_cloexec {
                    *old_flag = flag;
                } else {
                    throw_unsup_format!("Unsupported arg {:#x} for `F_SETFD`", flag);
                }
            }
            Ok(0)
        } else if cmd == this.eval_libc_i32("F_GETFD")? {
            if let Some(handle) = this.machine.file_handler.handles.get(&fd) {
                Ok(handle.flag)
            } else {
                this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
                Ok(-1)
            }
        } else {
            throw_unsup_format!("Unsupported command {:#x}", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            this.consume_result::<i32>(handle.file.sync_all().map(|_| 0), -1)
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            Ok(-1)
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let tcx = &{ this.tcx.tcx };

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;
        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;

        let mut bytes = vec![0; count as usize];

        let read_result = if let Some(FileHandle { file, ..}) = this.machine.file_handler.handles.get_mut(&fd) {
            file.read(&mut bytes).map(|bytes| bytes as i64)
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            return Ok(-1);
        };

        let read_bytes = this.consume_result::<i64>(read_result, -1)?;
        if read_bytes != -1 {
            bytes.truncate(read_bytes as usize);
            this.memory_mut()
                .get_mut(buf.alloc_id)?
                .write_bytes(tcx, buf, &bytes)?;
        }
        Ok(read_bytes)
    }

    fn consume_result<T>(&mut self, result: std::io::Result<T>, t: T) -> InterpResult<'tcx, T> {
        match result {
            Ok(ok) => Ok(ok),
            Err(e) => {
                self.eval_context_mut().machine.last_error = e.raw_os_error().unwrap() as u32;
                Ok(t)
            }
        }
    }
}
<|endoftext|>


<commit_before>/**
    Converts a vector of types that have the ToString trait into
    a pretty string
*/
pub fn string_for_vector<T: ToString>(vec: Vec<T>) -> String {
    let mut s = String::new();
    for x in &vec {
        s.push_str(&x.to_string());
        s.push(',');
    }
    s.pop();
    s
}
<commit_msg>Further module work<commit_after>///
/// Converts a vector of types that have the ToString trait into
/// a pretty string
///
/// # Arguments
///
/// * 'vec' - The vector you'd like a pretty string for
///
/// # Examples
///
/// ```rust
/// let v = vec![1,2,3]
/// string_for_vector(v) // prints "1,2,3"
/// ```
///
pub fn string_for_vector<T: ToString>(vec: Vec<T>) -> String {
    let mut s = String::new();
    for x in &vec {
        s.push_str(&x.to_string());
        s.push(',');
    }
    s.pop();
    s
}
<|endoftext|>


<commit_before>//! Future-aware synchronization
//!
//! This module, which is modeled after `std::sync`, contains user-space
//! synchronization tools that work with futures, streams and sinks. In
//! particular, these synchronizers do *not* block physical OS threads, but
//! instead work at the task level.

pub mod oneshot;
pub mod spsc;
mod bilock;

pub use self::bilock::BiLock;
<commit_msg>Export BiLockAcquire and BiLockAcquired.<commit_after>//! Future-aware synchronization
//!
//! This module, which is modeled after `std::sync`, contains user-space
//! synchronization tools that work with futures, streams and sinks. In
//! particular, these synchronizers do *not* block physical OS threads, but
//! instead work at the task level.

pub mod oneshot;
pub mod spsc;
mod bilock;

pub use self::bilock::{BiLock, BiLockAcquire, BiLockAcquired};
<|endoftext|>


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

use termion;
use termion::raw::{IntoRawMode, RawTerminal};

use rustbox;

use buffer::{Buffer, Cell};
use layout::{Rect, Group, split};
use widgets::Widget;
use style::{Color, Modifier, Style};

pub trait Backend {
    fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
        where I: Iterator<Item = (u16, u16, &'a Cell)>;
    fn hide_cursor(&mut self) -> Result<(), io::Error>;
    fn show_cursor(&mut self) -> Result<(), io::Error>;
    fn clear(&mut self) -> Result<(), io::Error>;
    fn size(&self) -> Result<Rect, io::Error>;
    fn flush(&mut self) -> Result<(), io::Error>;
}

pub struct TermionBackend {
    stdout: RawTerminal<io::Stdout>,
}

impl TermionBackend {
    pub fn new() -> Result<TermionBackend, io::Error> {
        let stdout = try!(io::stdout().into_raw_mode());
        Ok(TermionBackend { stdout: stdout })
    }
}

impl Backend for TermionBackend {
    /// Clears the entire screen and move the cursor to the top left of the screen
    fn clear(&mut self) -> Result<(), io::Error> {
        try!(write!(self.stdout, "{}", termion::clear::All));
        try!(write!(self.stdout, "{}", termion::cursor::Goto(1, 1)));
        try!(self.stdout.flush());
        Ok(())
    }

    /// Hides cursor
    fn hide_cursor(&mut self) -> Result<(), io::Error> {
        try!(write!(self.stdout, "{}", termion::cursor::Hide));
        try!(self.stdout.flush());
        Ok(())
    }

    /// Shows cursor
    fn show_cursor(&mut self) -> Result<(), io::Error> {
        try!(write!(self.stdout, "{}", termion::cursor::Show));
        try!(self.stdout.flush());
        Ok(())
    }

    fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
        where I: Iterator<Item = (u16, u16, &'a Cell)>
    {
        let mut string = String::with_capacity(content.size_hint().0 * 3);
        let mut style = Style::default();
        let mut last_y = 0;
        let mut last_x = 0;
        let mut inst = 0;
        for (x, y, cell) in content {
            if y != last_y || x != last_x + 1 {
                string.push_str(&format!("{}", termion::cursor::Goto(x + 1, y + 1)));
                inst += 1;
            }
            last_x = x;
            last_y = y;
            if cell.style.modifier != style.modifier {
                string.push_str(&cell.style.modifier.termion_modifier());
                style.modifier = cell.style.modifier;
                if style.modifier == Modifier::Reset {
                    style.bg = Color::Reset;
                    style.fg = Color::Reset;
                }
                inst += 1;
            }
            if cell.style.fg != style.fg {
                string.push_str(&cell.style.fg.termion_fg());
                style.fg = cell.style.fg;
                inst += 1;
            }
            if cell.style.bg != style.bg {
                string.push_str(&cell.style.bg.termion_bg());
                style.bg = cell.style.bg;
                inst += 1;
            }
            string.push_str(&cell.symbol);
            inst += 1;
        }
        debug!("{} instructions outputed.", inst);
        try!(write!(self.stdout,
                    "{}{}{}{}",
                    string,
                    Color::Reset.termion_fg(),
                    Color::Reset.termion_bg(),
                    Modifier::Reset.termion_modifier()));
        Ok(())
    }

    /// Return the size of the terminal
    fn size(&self) -> Result<Rect, io::Error> {
        let terminal = try!(termion::terminal_size());
        Ok(Rect {
            x: 0,
            y: 0,
            width: terminal.0,
            height: terminal.1,
        })
    }

    fn flush(&mut self) -> Result<(), io::Error> {
        try!(self.stdout.flush());
        Ok(())
    }
}

pub struct RustboxBackend {
    rustbox: rustbox::RustBox,
}

impl RustboxBackend {
    pub fn new() -> Result<RustboxBackend, rustbox::InitError> {
        let rustbox = try!(rustbox::RustBox::init(Default::default()));
        Ok(RustboxBackend { rustbox: rustbox })
    }

    pub fn rustbox(&self) -> &rustbox::RustBox {
        &self.rustbox
    }
}

impl Backend for RustboxBackend {
    fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
        where I: Iterator<Item = (u16, u16, &'a Cell)>
    {
        let mut inst = 0;
        for (x, y, cell) in content {
            inst += 1;
            self.rustbox.print(x as usize,
                               y as usize,
                               cell.style.modifier.into(),
                               cell.style.fg.into(),
                               cell.style.bg.into(),
                               &cell.symbol);
        }
        debug!("{} instructions outputed", inst);
        Ok(())
    }
    fn hide_cursor(&mut self) -> Result<(), io::Error> {
        Ok(())
    }
    fn show_cursor(&mut self) -> Result<(), io::Error> {
        Ok(())
    }
    fn clear(&mut self) -> Result<(), io::Error> {
        self.rustbox.clear();
        Ok(())
    }
    fn size(&self) -> Result<Rect, io::Error> {
        Ok((Rect {
            x: 0,
            y: 0,
            width: self.rustbox.width() as u16,
            height: self.rustbox.height() as u16,
        }))
    }
    fn flush(&mut self) -> Result<(), io::Error> {
        self.rustbox.present();
        Ok(())
    }
}

/// Holds a computed layout and keeps track of its use between successive draw calls
#[derive(Debug)]
pub struct LayoutEntry {
    chunks: Vec<Rect>,
    hot: bool,
}

/// Interface to the terminal backed by Termion
pub struct Terminal<B>
    where B: Backend
{
    backend: B,
    /// Cache to prevent the layout to be computed at each draw call
    layout_cache: HashMap<(Group, Rect), LayoutEntry>,
    /// Holds the results of the current and previous draw calls. The two are compared at the end
    /// of each draw pass to output the necessary updates to the terminal
    buffers: [Buffer; 2],
    /// Index of the current buffer in the previous array
    current: usize,
}

impl<B> Terminal<B>
    where B: Backend
{
    /// Wrapper around Termion initialization. Each buffer is initialized with a blank string and
    /// default colors for the foreground and the background
    pub fn new(backend: B) -> Result<Terminal<B>, io::Error> {
        let size = try!(backend.size());
        Ok(Terminal {
            backend: backend,
            layout_cache: HashMap::new(),
            buffers: [Buffer::empty(size), Buffer::empty(size)],
            current: 0,
        })
    }

    pub fn backend(&self) -> &B {
        &self.backend
    }

    pub fn backend_mut(&mut self) -> &mut B {
        &mut self.backend
    }

    /// Check if we have already computed a layout for a given group, otherwise it creates one and
    /// add it to the layout cache. Moreover the function marks the queried entries so that we can
    /// clean outdated ones at the end of the draw call.
    pub fn compute_layout(&mut self, group: &Group, area: &Rect) -> Vec<Rect> {
        let entry = self.layout_cache
            .entry((group.clone(), area.clone()))
            .or_insert_with(|| {
                let chunks = split(area, &group.direction, group.margin, &group.sizes);
                debug!("New layout computed:\n* Group = {:?}\n* Chunks = {:?}",
                       group,
                       chunks);
                LayoutEntry {
                    chunks: chunks,
                    hot: true,
                }
            });
        entry.hot = true;
        entry.chunks.clone()
    }

    /// Builds a string representing the minimal escape sequences and characters set necessary to
    /// update the UI and writes it to stdout.
    pub fn flush(&mut self) -> Result<(), io::Error> {
        let width = self.buffers[self.current].area.width;
        let content = self.buffers[self.current]
            .content
            .iter()
            .zip(self.buffers[1 - self.current].content.iter())
            .enumerate()
            .filter_map(|(i, (c, p))| if c != p {
                let i = i as u16;
                let x = i % width;
                let y = i / width;
                Some((x, y, c))
            } else {
                None
            });
        self.backend.draw(content)
    }

    /// Calls the draw method of a given widget on the current buffer
    pub fn render<W>(&mut self, widget: &W, area: &Rect)
        where W: Widget
    {
        widget.draw(area, &mut self.buffers[self.current]);
    }

    /// Updates the interface so that internal buffers matches the current size of the terminal.
    /// This leads to a full redraw of the screen.
    pub fn resize(&mut self, area: Rect) -> Result<(), io::Error> {
        self.buffers[self.current].resize(area);
        self.buffers[1 - self.current].resize(area);
        self.buffers[1 - self.current].reset();
        self.layout_cache.clear();
        try!(self.backend.clear());
        Ok(())
    }

    /// Flushes the current internal state and prepares the interface for the next draw call
    pub fn draw(&mut self) -> Result<(), io::Error> {

        // Draw to stdout
        try!(self.flush());

        // Clean layout cache
        let hot = self.layout_cache
            .drain()
            .filter(|&(_, ref v)| v.hot == true)
            .collect::<Vec<((Group, Rect), LayoutEntry)>>();

        for (key, value) in hot {
            self.layout_cache.insert(key, value);
        }

        for (_, e) in &mut self.layout_cache {
            e.hot = false;
        }

        // Swap buffers
        self.buffers[1 - self.current].reset();
        self.current = 1 - self.current;

        // Flush
        try!(self.backend.flush());
        Ok(())
    }

    pub fn hide_cursor(&mut self) -> Result<(), io::Error> {
        self.backend.hide_cursor()
    }
    pub fn show_cursor(&mut self) -> Result<(), io::Error> {
        self.backend.show_cursor()
    }
    pub fn clear(&mut self) -> Result<(), io::Error> {
        self.backend.clear()
    }
    pub fn size(&self) -> Result<Rect, io::Error> {
        self.backend.size()
    }
}
<commit_msg>Add the possibility to provide a RustBox instance at backend initialization<commit_after>use std::io;
use std::io::Write;
use std::collections::HashMap;

use termion;
use termion::raw::{IntoRawMode, RawTerminal};

use rustbox;

use buffer::{Buffer, Cell};
use layout::{Rect, Group, split};
use widgets::Widget;
use style::{Color, Modifier, Style};

pub trait Backend {
    fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
        where I: Iterator<Item = (u16, u16, &'a Cell)>;
    fn hide_cursor(&mut self) -> Result<(), io::Error>;
    fn show_cursor(&mut self) -> Result<(), io::Error>;
    fn clear(&mut self) -> Result<(), io::Error>;
    fn size(&self) -> Result<Rect, io::Error>;
    fn flush(&mut self) -> Result<(), io::Error>;
}

pub struct TermionBackend {
    stdout: RawTerminal<io::Stdout>,
}

impl TermionBackend {
    pub fn new() -> Result<TermionBackend, io::Error> {
        let stdout = try!(io::stdout().into_raw_mode());
        Ok(TermionBackend { stdout: stdout })
    }
}

impl Backend for TermionBackend {
    /// Clears the entire screen and move the cursor to the top left of the screen
    fn clear(&mut self) -> Result<(), io::Error> {
        try!(write!(self.stdout, "{}", termion::clear::All));
        try!(write!(self.stdout, "{}", termion::cursor::Goto(1, 1)));
        try!(self.stdout.flush());
        Ok(())
    }

    /// Hides cursor
    fn hide_cursor(&mut self) -> Result<(), io::Error> {
        try!(write!(self.stdout, "{}", termion::cursor::Hide));
        try!(self.stdout.flush());
        Ok(())
    }

    /// Shows cursor
    fn show_cursor(&mut self) -> Result<(), io::Error> {
        try!(write!(self.stdout, "{}", termion::cursor::Show));
        try!(self.stdout.flush());
        Ok(())
    }

    fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
        where I: Iterator<Item = (u16, u16, &'a Cell)>
    {
        let mut string = String::with_capacity(content.size_hint().0 * 3);
        let mut style = Style::default();
        let mut last_y = 0;
        let mut last_x = 0;
        let mut inst = 0;
        for (x, y, cell) in content {
            if y != last_y || x != last_x + 1 {
                string.push_str(&format!("{}", termion::cursor::Goto(x + 1, y + 1)));
                inst += 1;
            }
            last_x = x;
            last_y = y;
            if cell.style.modifier != style.modifier {
                string.push_str(&cell.style.modifier.termion_modifier());
                style.modifier = cell.style.modifier;
                if style.modifier == Modifier::Reset {
                    style.bg = Color::Reset;
                    style.fg = Color::Reset;
                }
                inst += 1;
            }
            if cell.style.fg != style.fg {
                string.push_str(&cell.style.fg.termion_fg());
                style.fg = cell.style.fg;
                inst += 1;
            }
            if cell.style.bg != style.bg {
                string.push_str(&cell.style.bg.termion_bg());
                style.bg = cell.style.bg;
                inst += 1;
            }
            string.push_str(&cell.symbol);
            inst += 1;
        }
        debug!("{} instructions outputed.", inst);
        try!(write!(self.stdout,
                    "{}{}{}{}",
                    string,
                    Color::Reset.termion_fg(),
                    Color::Reset.termion_bg(),
                    Modifier::Reset.termion_modifier()));
        Ok(())
    }

    /// Return the size of the terminal
    fn size(&self) -> Result<Rect, io::Error> {
        let terminal = try!(termion::terminal_size());
        Ok(Rect {
            x: 0,
            y: 0,
            width: terminal.0,
            height: terminal.1,
        })
    }

    fn flush(&mut self) -> Result<(), io::Error> {
        try!(self.stdout.flush());
        Ok(())
    }
}

pub struct RustboxBackend {
    rustbox: rustbox::RustBox,
}

impl RustboxBackend {
    pub fn new() -> Result<RustboxBackend, rustbox::InitError> {
        let rustbox = try!(rustbox::RustBox::init(Default::default()));
        Ok(RustboxBackend { rustbox: rustbox })
    }

    pub fn with_rustbox(instance: rustbox::RustBox) -> RustboxBackend {
        RustboxBackend { rustbox: instance }
    }

    pub fn rustbox(&self) -> &rustbox::RustBox {
        &self.rustbox
    }
}

impl Backend for RustboxBackend {
    fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
        where I: Iterator<Item = (u16, u16, &'a Cell)>
    {
        let mut inst = 0;
        for (x, y, cell) in content {
            inst += 1;
            self.rustbox.print(x as usize,
                               y as usize,
                               cell.style.modifier.into(),
                               cell.style.fg.into(),
                               cell.style.bg.into(),
                               &cell.symbol);
        }
        debug!("{} instructions outputed", inst);
        Ok(())
    }
    fn hide_cursor(&mut self) -> Result<(), io::Error> {
        Ok(())
    }
    fn show_cursor(&mut self) -> Result<(), io::Error> {
        Ok(())
    }
    fn clear(&mut self) -> Result<(), io::Error> {
        self.rustbox.clear();
        Ok(())
    }
    fn size(&self) -> Result<Rect, io::Error> {
        Ok((Rect {
            x: 0,
            y: 0,
            width: self.rustbox.width() as u16,
            height: self.rustbox.height() as u16,
        }))
    }
    fn flush(&mut self) -> Result<(), io::Error> {
        self.rustbox.present();
        Ok(())
    }
}

/// Holds a computed layout and keeps track of its use between successive draw calls
#[derive(Debug)]
pub struct LayoutEntry {
    chunks: Vec<Rect>,
    hot: bool,
}

/// Interface to the terminal backed by Termion
pub struct Terminal<B>
    where B: Backend
{
    backend: B,
    /// Cache to prevent the layout to be computed at each draw call
    layout_cache: HashMap<(Group, Rect), LayoutEntry>,
    /// Holds the results of the current and previous draw calls. The two are compared at the end
    /// of each draw pass to output the necessary updates to the terminal
    buffers: [Buffer; 2],
    /// Index of the current buffer in the previous array
    current: usize,
}

impl<B> Terminal<B>
    where B: Backend
{
    /// Wrapper around Termion initialization. Each buffer is initialized with a blank string and
    /// default colors for the foreground and the background
    pub fn new(backend: B) -> Result<Terminal<B>, io::Error> {
        let size = try!(backend.size());
        Ok(Terminal {
            backend: backend,
            layout_cache: HashMap::new(),
            buffers: [Buffer::empty(size), Buffer::empty(size)],
            current: 0,
        })
    }

    pub fn backend(&self) -> &B {
        &self.backend
    }

    pub fn backend_mut(&mut self) -> &mut B {
        &mut self.backend
    }

    /// Check if we have already computed a layout for a given group, otherwise it creates one and
    /// add it to the layout cache. Moreover the function marks the queried entries so that we can
    /// clean outdated ones at the end of the draw call.
    pub fn compute_layout(&mut self, group: &Group, area: &Rect) -> Vec<Rect> {
        let entry = self.layout_cache
            .entry((group.clone(), area.clone()))
            .or_insert_with(|| {
                let chunks = split(area, &group.direction, group.margin, &group.sizes);
                debug!("New layout computed:\n* Group = {:?}\n* Chunks = {:?}",
                       group,
                       chunks);
                LayoutEntry {
                    chunks: chunks,
                    hot: true,
                }
            });
        entry.hot = true;
        entry.chunks.clone()
    }

    /// Builds a string representing the minimal escape sequences and characters set necessary to
    /// update the UI and writes it to stdout.
    pub fn flush(&mut self) -> Result<(), io::Error> {
        let width = self.buffers[self.current].area.width;
        let content = self.buffers[self.current]
            .content
            .iter()
            .zip(self.buffers[1 - self.current].content.iter())
            .enumerate()
            .filter_map(|(i, (c, p))| if c != p {
                let i = i as u16;
                let x = i % width;
                let y = i / width;
                Some((x, y, c))
            } else {
                None
            });
        self.backend.draw(content)
    }

    /// Calls the draw method of a given widget on the current buffer
    pub fn render<W>(&mut self, widget: &W, area: &Rect)
        where W: Widget
    {
        widget.draw(area, &mut self.buffers[self.current]);
    }

    /// Updates the interface so that internal buffers matches the current size of the terminal.
    /// This leads to a full redraw of the screen.
    pub fn resize(&mut self, area: Rect) -> Result<(), io::Error> {
        self.buffers[self.current].resize(area);
        self.buffers[1 - self.current].resize(area);
        self.buffers[1 - self.current].reset();
        self.layout_cache.clear();
        try!(self.backend.clear());
        Ok(())
    }

    /// Flushes the current internal state and prepares the interface for the next draw call
    pub fn draw(&mut self) -> Result<(), io::Error> {

        // Draw to stdout
        try!(self.flush());

        // Clean layout cache
        let hot = self.layout_cache
            .drain()
            .filter(|&(_, ref v)| v.hot == true)
            .collect::<Vec<((Group, Rect), LayoutEntry)>>();

        for (key, value) in hot {
            self.layout_cache.insert(key, value);
        }

        for (_, e) in &mut self.layout_cache {
            e.hot = false;
        }

        // Swap buffers
        self.buffers[1 - self.current].reset();
        self.current = 1 - self.current;

        // Flush
        try!(self.backend.flush());
        Ok(())
    }

    pub fn hide_cursor(&mut self) -> Result<(), io::Error> {
        self.backend.hide_cursor()
    }
    pub fn show_cursor(&mut self) -> Result<(), io::Error> {
        self.backend.show_cursor()
    }
    pub fn clear(&mut self) -> Result<(), io::Error> {
        self.backend.clear()
    }
    pub fn size(&self) -> Result<Rect, io::Error> {
        self.backend.size()
    }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;

use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::mpsc::Receiver;

use std::fmt;

type WordCell = Rc<RefCell<Word>>;
pub type AuthorCell = Rc<RefCell<Author>>;
pub type SourceCell = Rc<RefCell<Source>>;
pub type MessageCell = Rc<RefCell<Message>>;
type CategoryCell = Rc<RefCell<Category>>;
type ConversationCell = Rc<RefCell<Conversation>>;
type InstanceCell = Rc<RefCell<WordInstance>>;

enum Mismatch<T> {
    // Incompatible for matching or no mismatch (exactly the same)
    None,
    // Exactly one mismatch
    One(T),
    // Multiple mismatches
    Multiple,
}

fn wrap<T>(t: T) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(t))
}

// const RATIO_CONTAINED_BEFORE_COMBINATION: f64 = 0.8;

#[derive(Default)]
pub struct Lexicon<R: rand::Rng> {
    rng: R,
    words: BTreeMap<String, WordCell>,
    sources: BTreeMap<String, SourceCell>,
    conversations: Vec<ConversationCell>,
    messages: Vec<MessageCell>,

    active_conversations: BTreeMap<*const Source, ConversationCell>,
}

impl<R: rand::Rng> Lexicon<R> {
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) -> Option<String> {
        let conversation = match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // Learn the message immediately
        self.learn(message);

        // TODO: Determine what to say back
        None
    }

    pub fn initiate(&mut self, source: SourceCell) -> Option<String> {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());

        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        let bborrow = base.borrow();

        Some(format!("{} ~ {}",
            bborrow.instances.iter()
                .map(|instance| instance.borrow().category.clone())
                // TODO: Allow random choosing from co-category instances as well
                .map(|category| self.rng.choose(&category.borrow().instances[..]).unwrap().clone())
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            bborrow.instances.iter()
                .map(|i| {
                    let b = i.borrow();
                    let b = b.word.borrow();
                    b.name.clone()
                })
                .join(" "))
        )
    }

    pub fn think(&mut self, end: Receiver<()>) {
        use std::sync::mpsc::TryRecvError::Empty;
        while end.try_recv() == Err(Empty) {
            // Choose a random message or break if there are none
            let message = match self.rng.choose(&self.messages[..]) {
                Some(m) => m.clone(),
                None => break,
            };

            self.learn(message);
        }
    }

    pub fn learn(&mut self, message: MessageCell) {
        // Vector of absolute perfect matches
        let mut vones = Vec::new();

        // Look through each word in the message
        for word in &message.borrow().instances {
            let borrow = word.borrow();
            // Check each instance in that words instances
            for instance in &borrow.word.borrow().instances {
                // Get the message for each instance
                let omessage = instance.borrow().message.clone();
                // Find what kind of matches exist between the messages
                if let Mismatch::One(best) =
                    Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                    vones.push(best);
                }
            }
        }

        // Now that we have perfect matches, merge them into the same Category
        for ms in vones {
            // We only want to combine if they aren't already in the same category
            if ms.0.borrow().category != ms.1.borrow().category {
                let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                Category::merge(cats);
            }
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self) -> usize {
        use std::collections::BTreeSet;
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(&*ib.category.borrow() as *const Category);
            }
        }

        let len = set.len();

        for cat in set {
            let catr = unsafe{&*cat};
            if catr.instances.len() != 1 {
                println!("Category:");
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    println!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow());
                }
            }
        }
        len
    }
}

macro_rules! pointer_eq {
    ($s:ident) => {
        impl PartialEq for $s {
            fn eq(&self, other: &Self) -> bool {
                self as *const Self == other as *const Self
            }

            fn ne(&self, other: &Self) -> bool {
                self as *const Self != other as *const Self
            }
        }
    };
}

struct Conversation {
    source: SourceCell,
    messages: Vec<MessageCell>,
}

pointer_eq!(Conversation);

pub struct Author {
    source: SourceCell,
    name: String,
}

pointer_eq!(Author);

pub struct Source {
    name: String,
    messages: u64,
    authors: BTreeMap<String, AuthorCell>,
}

pointer_eq!(Source);

struct WordInstance {
    word: WordCell,
    category: CategoryCell,
    message: MessageCell,
    index: usize,
}

pointer_eq!(WordInstance);

pub struct Message {
    author: AuthorCell,
    conversation: ConversationCell,
    index: usize,
    instances: Vec<InstanceCell>,
}

impl Message {
    fn string(&self) -> String {
        self.instances.iter()
            .map(|i| {
                let b = i.borrow();
                let b = b.word.borrow();
                b.name.clone()
            })
            .join(" ")
    }
    fn mismatch<F>(messages: (MessageCell, MessageCell), diff: F) -> Mismatch<(InstanceCell, InstanceCell)>
        where F: Fn((&WordInstance, &WordInstance)) -> bool
    {
        if messages.0 == messages.1 ||
            messages.0.borrow().instances.len() != messages.1.borrow().instances.len() {
            return Mismatch::None;
        }
        messages.0.borrow().instances.iter()
            .zip(messages.1.borrow().instances.iter())
            .fold(Mismatch::None, |acc, ws| {
                let bs = (ws.0.borrow(), ws.1.borrow());
                match acc {
                    Mismatch::None => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::One((ws.0.clone(), ws.1.clone()))
                        } else {
                            Mismatch::None
                        }
                    },
                    Mismatch::One(best) => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::Multiple
                        } else {
                            Mismatch::One(best)
                        }
                    },
                    Mismatch::Multiple => Mismatch::Multiple,
                }
            })
    }
    /*fn category_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.category != ins.1.category)
    }

    fn word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word)
    }*/

    fn category_and_word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word && ins.0.category != ins.1.category)
    }
}

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

pointer_eq!(Message);

#[derive(Default)]
struct Category {
    instances: Vec<InstanceCell>,
    cocategories: Vec<CategoryCell>,
}

impl Category {
    fn merge(cs: (CategoryCell, CategoryCell)) -> CategoryCell {
        let cc = wrap(Self::default());
        {
            let clos = |cat: &CategoryCell| {
                for instance in cat.borrow().instances.iter().cloned() {
                    instance.borrow_mut().category = cc.clone();
                }
            };
            clos(&cs.0);
            clos(&cs.1);
            let mut ccm = cc.borrow_mut();
            ccm.instances.append(&mut cs.0.borrow_mut().instances);
            ccm.instances.append(&mut cs.1.borrow_mut().instances);
        }
        cc
    }
}

pointer_eq!(Category);

struct Word {
    name: String,
    instances: Vec<InstanceCell>,
}

pointer_eq!(Word);
<commit_msg>cocategories initially complete<commit_after>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;

use std::collections::{BTreeMap, BTreeSet};
use std::collections::btree_map::Entry;
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::mpsc::Receiver;
use std::cmp;

use std::fmt;

const RATIO_TO_COCATEGORIZE: f64 = 0.8;

pub type WordCell = Rc<RefCell<Word>>;
pub type AuthorCell = Rc<RefCell<Author>>;
pub type SourceCell = Rc<RefCell<Source>>;
pub type MessageCell = Rc<RefCell<Message>>;
pub type CategoryCell = Rc<RefCell<Category>>;
pub type ConversationCell = Rc<RefCell<Conversation>>;
pub type InstanceCell = Rc<RefCell<WordInstance>>;

enum Mismatch<T> {
    // Incompatible for matching or no mismatch (exactly the same)
    None,
    // Exactly one mismatch
    One(T),
    // Multiple mismatches
    Multiple,
}

fn wrap<T>(t: T) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(t))
}

#[derive(Default)]
pub struct Lexicon<R: rand::Rng> {
    rng: R,
    words: BTreeMap<String, WordCell>,
    sources: BTreeMap<String, SourceCell>,
    conversations: Vec<ConversationCell>,
    messages: Vec<MessageCell>,

    active_conversations: BTreeMap<*const Source, ConversationCell>,
}

impl<R: rand::Rng> Lexicon<R> {
    /// Make a new lexion. It needs its own Rng for internal purposes of learning.
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    /// Get a source by its unique name.
    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    /// Get an author identifier from a particular source.
    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    /// Tell a message to the lexicon and potentially get a response back.
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) -> Option<String> {
        let conversation = match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // Learn the message immediately
        self.learn(message);

        // TODO: Determine what to say back
        None
    }

    /// Have seifmios attempt to initiate a conversation at a source, but it may fail.
    pub fn initiate(&mut self, source: SourceCell) -> Option<String> {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());

        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        let bborrow = base.borrow();

        Some(
            bborrow.instances.iter()
                .map(|instance| instance.borrow().category.clone())
                // TODO: Allow random choosing from co-category instances as well
                .map(|category| {
                    let b = category.borrow();
                    // Find the count of how many word instances exist total
                    let mut count = b.instances.len();
                    for cocategory in &b.cocategories {
                        count += cocategory.borrow().instances.len();
                    }
                    // Generate an index based on the count
                    let mut i = self.rng.gen_range(0, count);
                    match b.instances.get(i) {
                        // It was in the original category
                        Some(ins) => ins.clone(),
                        // It was in a cocategory
                        None => {
                            // Subtract the cocategory length from the index
                            i -= b.instances.len();
                            for cocategory in &b.cocategories {
                                let b = cocategory.borrow();
                                // If it was in this category
                                if let Some(ins) = b.instances.get(i) {
                                    // Clone the instance and return it
                                    return ins.clone();
                                }
                                // Otherwise subtract by the amount of instances in this cocategory
                                i -= b.instances.len();
                            }
                            // The index should point to some category, so this is unreachable
                            unreachable!();
                        },
                    }
                })
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" ")
        )
    }

    pub fn think(&mut self, end: Receiver<()>) {
        use std::sync::mpsc::TryRecvError::Empty;
        while end.try_recv() == Err(Empty) {
            // Learn a random message if there are some
            let m = match self.rng.choose(&self.messages[..]) {
                Some(m) => m.clone(),
                None => break,
            };

            self.learn(m);

            // Get two random categories (we already know messages exist from above)
            Category::cocategorize((
                {
                    let b = self.rng.choose(&self.messages[..]).unwrap().borrow();
                    let b = self.rng.choose(&b.instances[..]).unwrap().borrow();
                    b.category.clone()
                },
                {
                    let b = self.rng.choose(&self.messages[..]).unwrap().borrow();
                    let b = self.rng.choose(&b.instances[..]).unwrap().borrow();
                    b.category.clone()
                },
            ));
        }
    }

    pub fn learn(&mut self, message: MessageCell) {
        // Vector of absolute perfect matches
        let mut vones = Vec::new();

        // Look through each word in the message
        for word in &message.borrow().instances {
            let borrow = word.borrow();
            // Check each instance in that words instances
            for instance in &borrow.word.borrow().instances {
                // Get the message for each instance
                let omessage = instance.borrow().message.clone();
                // Find what kind of matches exist between the messages
                if let Mismatch::One(best) =
                    Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                    vones.push(best);
                }
            }
        }

        // Now that we have perfect matches, merge them into the same Category
        for ms in vones {
            // We only want to combine if they aren't already in the same category
            if ms.0.borrow().category != ms.1.borrow().category {
                let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                Category::merge(cats);
            }
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self) -> usize {
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(&*ib.category.borrow() as *const Category);
            }
        }

        let len = set.len();

        for cat in set {
            let catr = unsafe{&*cat};
            if catr.instances.len() != 1 {
                println!("Category:");
                for cocategory in &catr.cocategories {
                    println!("\tCocategory:");
                    let catr = cocategory.borrow();
                    for instance in &catr.instances {
                        let ib = instance.borrow();
                        println!("\t\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow());
                    }
                }
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    println!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow());
                }
            }
        }
        len
    }
}

macro_rules! pointer_ord {
    ($s:ident) => {
        // Implement on the type
        impl PartialEq for $s {
            fn eq(&self, other: &Self) -> bool {
                self as *const Self == other as *const Self
            }

            fn ne(&self, other: &Self) -> bool {
                self as *const Self != other as *const Self
            }
        }

        impl Eq for $s {}

        impl PartialOrd for $s {
            fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
                (self as *const Self).partial_cmp(&(other as *const Self))
            }

            fn lt(&self, other: &Self) -> bool {
                (self as *const Self) < (other as *const Self)
            }

            fn le(&self, other: &Self) -> bool {
                self as *const Self <= other as *const Self
            }

            fn gt(&self, other: &Self) -> bool {
                self as *const Self > other as *const Self
            }

            fn ge(&self, other: &Self) -> bool {
                self as *const Self >= other as *const Self
            }
        }

        impl Ord for $s {
            fn cmp(&self, other: &Self) -> cmp::Ordering {
                (self as *const Self).cmp(&(other as *const Self))
            }
        }
    };
}

pub struct Conversation {
    source: SourceCell,
    messages: Vec<MessageCell>,
}

pointer_ord!(Conversation);

pub struct Author {
    source: SourceCell,
    name: String,
}

pointer_ord!(Author);

pub struct Source {
    name: String,
    messages: u64,
    authors: BTreeMap<String, AuthorCell>,
}

pointer_ord!(Source);

pub struct WordInstance {
    word: WordCell,
    category: CategoryCell,
    message: MessageCell,
    index: usize,
}

impl WordInstance {
    fn coincidence_level(ins: (&InstanceCell, &InstanceCell)) -> usize {
        let bs = (ins.0.borrow(), ins.1.borrow());
        let ms = (bs.0.message.borrow(), bs.1.message.borrow());
        for i in 1.. {
            let msins = (
                (ms.0.instances.get(bs.0.index - i), ms.1.instances.get(bs.1.index - i)),
                (ms.0.instances.get(bs.0.index + i), ms.1.instances.get(bs.1.index + i))
            );

            match msins.0 {
                // There are two words
                (Some(i0), Some(i1)) => {
                    // If both the categories and words don't match
                    if i0.borrow().category != i1.borrow().category
                        && i0.borrow().word != i1.borrow().word {
                        // The coincidence level doesn't go this far
                        return i-1;
                    }
                },
                // The sentence ends in both spots
                (None, None) => {
                    // We can't go any further, but we also need to check the right instances
                    match msins.1 {
                        (Some(i0), Some(i1)) => {
                            // If both the categories and words don't match
                            if i0.borrow().category != i1.borrow().category
                                && i0.borrow().word != i1.borrow().word {
                                // The coincidence level doesn't go this far
                                return i-1;
                            }
                        },
                        (None, None) => {
                            // We can't go any further on either side, so this is the coincidence
                            return i;
                        },
                        _ => {
                            // Any combination of Some and None is a mismatch
                            return i-1;
                        }
                    }
                }
                _ => {
                    // Any combination of Some and None is a mismatch
                    return i-1;
                }
            }
        }
        unreachable!();
    }
}

pointer_ord!(WordInstance);

pub struct Message {
    author: AuthorCell,
    conversation: ConversationCell,
    index: usize,
    instances: Vec<InstanceCell>,
}

impl Message {
    fn string(&self) -> String {
        self.instances.iter()
            .map(|i| {
                let b = i.borrow();
                let b = b.word.borrow();
                b.name.clone()
            })
            .join(" ")
    }
    fn mismatch<F>(messages: (MessageCell, MessageCell), diff: F) -> Mismatch<(InstanceCell, InstanceCell)>
        where F: Fn((&WordInstance, &WordInstance)) -> bool
    {
        if messages.0 == messages.1 ||
            messages.0.borrow().instances.len() != messages.1.borrow().instances.len() {
            return Mismatch::None;
        }
        messages.0.borrow().instances.iter()
            .zip(messages.1.borrow().instances.iter())
            .fold(Mismatch::None, |acc, ws| {
                let bs = (ws.0.borrow(), ws.1.borrow());
                match acc {
                    Mismatch::None => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::One((ws.0.clone(), ws.1.clone()))
                        } else {
                            Mismatch::None
                        }
                    },
                    Mismatch::One(best) => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::Multiple
                        } else {
                            Mismatch::One(best)
                        }
                    },
                    Mismatch::Multiple => Mismatch::Multiple,
                }
            })
    }
    /*fn category_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.category != ins.1.category)
    }

    fn word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word)
    }*/

    fn category_and_word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word && ins.0.category != ins.1.category)
    }
}

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

pointer_ord!(Message);

#[derive(Default)]
pub struct Category {
    instances: Vec<InstanceCell>,
    cocategories: Vec<CategoryCell>,
}

impl Category {
    fn merge(cs: (CategoryCell, CategoryCell)) -> CategoryCell {
        let cc = wrap(Self::default());
        {
            let clos = |cat: &CategoryCell| {
                for instance in cat.borrow().instances.iter().cloned() {
                    instance.borrow_mut().category = cc.clone();
                }
            };
            clos(&cs.0);
            clos(&cs.1);
            let mut ccm = cc.borrow_mut();
            ccm.instances.append(&mut cs.0.borrow_mut().instances);
            ccm.instances.append(&mut cs.1.borrow_mut().instances);
        }
        cc
    }

    /// Determine if the categories should be cocategories
    fn cocategorize(cs: (CategoryCell, CategoryCell)) {
        // First, check to see if they are the same category
        if cs.0 == cs.1 {
            // Nothing to do in that case
            return;
        }

        // Get the total amount of instances in cs.0
        let total = cs.0.borrow().instances.len();
        // Make a counter to see how many instances coincide
        let mut coincidences = 0;

        // Look through all the instances between both categories
        {
            let bs = (cs.0.borrow(), cs.1.borrow());
            for i0 in bs.0.instances.iter() {
                // We see if there is any coincidence for this instance
                for i1 in bs.1.instances.iter() {
                    // It is impossible for two different categories to contain the same instance,
                    // so that doesn't need to be checked for.

                    // TODO: Look behind and ahead by more than just 1 instance
                    // Check if the coincidence level is at least 1 (for now)
                    if WordInstance::coincidence_level((i0, i1)) >= 1 {
                        // Increment the amount of coincidences
                        coincidences += 1;
                        // Break so we dont count any more
                        break;
                    }
                }
            }
        }

        // If the amount of coincidences is sufficient enough
        if coincidences as f64 / total as f64 > RATIO_TO_COCATEGORIZE {
            // Make these cocategories

            // Check if they are already cocategories
            if cs.0.borrow().cocategories.contains(&cs.1) {
                // Then we are done
                return;
            }

            // Add it
            cs.0.borrow_mut().cocategories.push(cs.1.clone());
            cs.1.borrow_mut().cocategories.push(cs.0.clone());
        } else {
            // Unmake these cocategories

            // Find position in vector of cocategories
            let csp0 = cs.0.borrow().cocategories.iter().position(|i| *i == cs.1);
            // If it wasnt found
            if csp0.is_none() {
                // Then we are done
                return;
            }
            let csp1 = cs.1.borrow().cocategories.iter().position(|i| *i == cs.0);

            // Remove it
            cs.0.borrow_mut().cocategories.swap_remove(csp0.unwrap());
            // In the case this panics, two cocategories didnt contain each other (bad stuff!)
            cs.1.borrow_mut().cocategories.swap_remove(csp1.unwrap());
        }
    }
}

pointer_ord!(Category);

pub struct Word {
    name: String,
    instances: Vec<InstanceCell>,
}

pointer_ord!(Word);
<|endoftext|>


<commit_before>// Copyright 2014 Arjan Topolovec
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[crate_id = "tftp"];
#[license = "MIT/ASL2"];
#[crate_type = "rlib"];
#[crate_type = "dylib"];
#[allow(deprecated_owned_vector)];
#[deny(warnings)];
#[feature(macro_rules, phase)];

extern crate collections;
extern crate rand;
#[phase(syntax, link)] extern crate log;

pub use common::TransferOptions;

pub mod protocol;

mod util;
mod common;
pub mod client;
<commit_msg>Changed crate attributes to new syntax<commit_after>// Copyright 2014 Arjan Topolovec
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![crate_id = "tftp"]
#![license = "MIT/ASL2"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![allow(deprecated_owned_vector)]
#![deny(warnings)]
#![feature(macro_rules, phase)]

extern crate collections;
extern crate rand;
#[phase(syntax, link)] extern crate log;

pub use common::TransferOptions;

pub mod protocol;

mod util;
mod common;
pub mod client;
<|endoftext|>


<commit_before>extern crate handlebars;

#[macro_use]
extern crate serde_json;

use handlebars::Handlebars;

#[test]
fn test_escape_216() {
    let hbs = Handlebars::new();

    let data = json!({
        "FOO": "foo",
        "BAR": "bar"
    });

    assert_eq!(
        hbs.render_template(r"\\\\ {{FOO}} {{BAR}} {{FOO}}{{BAR}} {{FOO}}#{{BAR}} {{FOO}}//{{BAR}} {{FOO}}\\{{FOO}} {{FOO}}\\\\{{FOO}}\\\{{FOO}} \\\{{FOO}} \{{FOO}} \{{FOO}}", &data).unwrap(),
        r"\\\\ foo bar foobar foo#bar foo//bar foo\foo foo\\\foo\\foo \\foo {{FOO}} {{FOO}}"
    );
}
<commit_msg>(test) add test to reproduce #422<commit_after>extern crate handlebars;

#[macro_use]
extern crate serde_json;

use handlebars::{handlebars_helper, Handlebars};

#[test]
fn test_escape_216() {
    let hbs = Handlebars::new();

    let data = json!({
        "FOO": "foo",
        "BAR": "bar"
    });

    assert_eq!(
        hbs.render_template(r"\\\\ {{FOO}} {{BAR}} {{FOO}}{{BAR}} {{FOO}}#{{BAR}} {{FOO}}//{{BAR}} {{FOO}}\\{{FOO}} {{FOO}}\\\\{{FOO}}\\\{{FOO}} \\\{{FOO}} \{{FOO}} \{{FOO}}", &data).unwrap(),
        r"\\\\ foo bar foobar foo#bar foo//bar foo\foo foo\\\foo\\foo \\foo {{FOO}} {{FOO}}"
    );
}

#[test]
fn test_string_no_escape_422() {
    let mut hbs = Handlebars::new();

    handlebars_helper!(replace: |input: str, from: str, to: str| {
        input.replace(from, to)
    });
    hbs.register_helper("replace", Box::new(replace));

    assert_eq!(
        r#"some\ path"#,
        hbs.render_template(r#"{{replace "some/path" "/" "\\ " }}"#, &())
            .unwrap()
    );

    assert_eq!(
        r#"some\path"#,
        hbs.render_template(r#"{{replace "some/path" "/" "\\" }}"#, &())
            .unwrap()
    );
}
<|endoftext|>


<commit_before>extern crate timely;
extern crate itertools;
extern crate differential_dataflow;

use timely::dataflow::operators::*;
use timely::dataflow::operators::capture::Extract;
use timely::progress::timestamp::RootTimestamp;
use timely::progress::nested::product::Product;
use differential_dataflow::collection::AsCollection;
use differential_dataflow::operators::arrange::{ArrangeByKey, Arrange};
use differential_dataflow::operators::group::GroupArranged;
use differential_dataflow::trace::implementations::ord::OrdValSpine;
use differential_dataflow::trace::{Trace, TraceReader};
use differential_dataflow::hashable::{OrdWrapper, UnsignedWrapper};
use itertools::Itertools;

type Result = std::sync::mpsc::Receiver<timely::dataflow::operators::capture::Event<timely::progress::nested::product::Product<timely::progress::timestamp::RootTimestamp, usize>, ((u64, i64), timely::progress::nested::product::Product<timely::progress::timestamp::RootTimestamp, usize>, i64)>>;

fn run_test<T>(test: T, expected: Vec<(Product<RootTimestamp, usize>, Vec<((u64, i64), i64)>)>) -> ()
        where T: FnOnce(Vec<Vec<((u64, u64), i64)>>)-> Result + ::std::panic::UnwindSafe
{
    let input_epochs: Vec<Vec<((u64, u64), i64)>> = vec![
        vec![((2, 0), 1), ((1, 0), 1), ((1, 3), 1), ((4, 2), 1)],
        vec![((2, 0), -1), ((1, 0), -1)],
        vec![((2, 0), 1), ((1, 3), -1)],
        vec![((1, 0), 1), ((1, 3), 1), ((2, 0), -1), ((4, 2), -1)],
        vec![((2, 0), 1), ((4, 2), 1), ((1, 3), -1)],
        vec![((1, 3), 1), ((4, 2), -1)],
    ];
    let captured = (test)(input_epochs);
    let mut results = captured.extract().into_iter().flat_map(|(_, data)| data).collect::<Vec<_>>();
    results.sort_by_key(|&(_, t, _)| t);
    let out = results.into_iter().group_by(|&(_, t, _)| t).into_iter()
        .map(|(t, vals)| (t, vals.map(|(v, _, w)| (v, w)).collect::<Vec<_>>())).collect::<Vec<_>>();
    // println!("out: {:?}", out);
    assert_eq!(out, expected);
}

#[test]
fn test_import() {
    run_test(|input_epochs| {
        timely::execute(timely::Configuration::Process(4), move |worker| {
            let ref input_epochs = input_epochs;
            let index = worker.index();
            let peers = worker.peers();

            let (mut input, mut trace) = worker.dataflow(|scope| {
                let (input, edges) = scope.new_input();
                let arranged = edges.as_collection()
                                    .arrange_by_key_hashed();
                (input, arranged.trace.clone())
            });
            let (captured,) = worker.dataflow(move |scope| {
                let imported = trace.import(scope);
                ::std::mem::drop(trace);
                let captured = imported.group_arranged::<_, i64, _, _>(|_k, s, t| t.push((s.iter().map(|&(_, w)| w).sum(), 1i64)), OrdValSpine::new())
                      .as_collection(|k: &OrdWrapper<u64>, c: &i64| (k.item.clone(), *c))
                      .inner.exchange(|_| 0)
                      .capture();
                (captured,)
            });

            for (t, changes) in input_epochs.into_iter().enumerate() {
                if t != input.time().inner {
                    input.advance_to(t);
                }
                let &time = input.time();
                for &((src, dst), w) in changes.into_iter().filter(|&&((src, _), _)| (src as usize) % peers == index) {
                    input.send(((src, dst), time, w));
                }
            }
            input.close();

            captured
        }).unwrap().join().into_iter().map(|x| x.unwrap()).next().unwrap()
    }, vec![
        (RootTimestamp::new(0), vec![
             ((1, 2), 1), ((2, 1), 1), ((4, 1), 1)]),
        (RootTimestamp::new(1), vec![
             ((1, 1), 1), ((1, 2), -1), ((2, 1), -1)]),
        (RootTimestamp::new(2), vec![
             ((1, 1), -1), ((2, 1), 1)]),
        (RootTimestamp::new(3), vec![
             ((1, 2), 1), ((2, 1), -1), ((4, 1), -1)]),
        (RootTimestamp::new(4), vec![
             ((1, 1), 1), ((1, 2), -1), ((2, 1), 1), ((4, 1), 1)]),
        (RootTimestamp::new(5), vec![
             ((1, 1), -1), ((1, 2), 1), ((4, 1), -1)]),
    ]);
}

#[test]
fn test_import_completed_dataflow() {
    // Runs the first dataflow to completion before constructing the subscriber.
    run_test(|input_epochs| {
        timely::execute(timely::Configuration::Process(4), move |worker| {
            let ref input_epochs = input_epochs;
            let index = worker.index();
            let peers = worker.peers();

            let (mut input, mut trace, probe) = worker.dataflow(|scope| {
                let (input, edges) = scope.new_input();
                let arranged = edges.as_collection()
                                    .arrange_by_key_hashed();
                (input, arranged.trace.clone(), arranged.stream.probe())
            });

            for (t, changes) in input_epochs.into_iter().enumerate() {
                if t != input.time().inner {
                    input.advance_to(t);
                }
                let &time = input.time();
                for &((src, dst), w) in changes.into_iter().filter(|&&((src, _), _)| (src as usize) % peers == index) {
                    input.send(((src, dst), time, w));
                }
            }
            input.close();

            worker.step_while(|| !probe.done());

            let (probe2, captured,) = worker.dataflow(move |scope| {
                let imported = trace.import(scope);
                ::std::mem::drop(trace);
                let stream = imported.group_arranged::<_, i64, _, _>(|_k, s, t| t.push((s.iter().map(|&(_, w)| w).sum(), 1i64)), OrdValSpine::new())
                      .as_collection(|k: &OrdWrapper<u64>, c: &i64| (k.item.clone(), *c))
                      .inner.exchange(|_| 0);
                let probe = stream.probe();
                let captured = stream.capture();
                (probe, captured,)
            });

            println!("probe2: {}", probe2.with_frontier(|f| format!("{:?}", f)));

            captured
        }).unwrap().join().into_iter().map(|x| x.unwrap()).next().unwrap()
    }, vec![
        (RootTimestamp::new(0), vec![
             ((1, 2), 1), ((2, 1), 1), ((4, 1), 1)]),
        (RootTimestamp::new(1), vec![
             ((1, 1), 1), ((1, 2), -1), ((2, 1), -1)]),
        (RootTimestamp::new(2), vec![
             ((1, 1), -1), ((2, 1), 1)]),
        (RootTimestamp::new(3), vec![
             ((1, 2), 1), ((2, 1), -1), ((4, 1), -1)]),
        (RootTimestamp::new(4), vec![
             ((1, 1), 1), ((1, 2), -1), ((2, 1), 1), ((4, 1), 1)]),
        (RootTimestamp::new(5), vec![
             ((1, 1), -1), ((1, 2), 1), ((4, 1), -1)]),
    ]);
}

#[ignore]
#[test]
fn import_skewed() {

    run_test(|_input| {
        let captured = timely::execute(timely::Configuration::Process(4), |worker| {
            let index = worker.index();
            let peers = worker.peers();

            let (mut input, mut trace) = worker.dataflow(|scope| {
                let (input, edges) = scope.new_input();
                let arranged = edges.as_collection()
                                    .map(|(k,v)| (UnsignedWrapper::from(k), v))
                                    .arrange(OrdValSpine::new());
                (input, arranged.trace.clone())
            });

            input.send(((index as u64, 1), RootTimestamp::new(index), 1));
            input.close();

            trace.advance_by(&[RootTimestamp::new(peers - index)]);

            let (captured,) = worker.dataflow(move |scope| {
                let imported = trace.import(scope);
                let captured = imported
                      .as_collection(|k: &UnsignedWrapper<u64>, c: &i64| (k.item.clone(), *c))
                      .inner.exchange(|_| 0)
                      .capture();
                (captured,)
            });

            captured
        }).unwrap().join().into_iter().map(|x| x.unwrap()).next().unwrap();

        // Worker `index` sent `index` at time `index`, but advanced its handle to `peers - index`.
        // As its data should be shuffled back to it (we used an UnsignedWrapper) this means that
        // `index` should be present at time `max(index, peers-index)`.

        captured
    }, vec![
        (RootTimestamp::new(2), vec![((2, 1), 1)]),
        (RootTimestamp::new(3), vec![((1, 1), 1), ((3, 1), 1)]),
        (RootTimestamp::new(4), vec![((0, 1), 1)]),
    ]);
}
<commit_msg>normalize (sort) data before assert_eq<commit_after>extern crate timely;
extern crate itertools;
extern crate differential_dataflow;

use timely::dataflow::operators::*;
use timely::dataflow::operators::capture::Extract;
use timely::progress::timestamp::RootTimestamp;
use timely::progress::nested::product::Product;
use differential_dataflow::collection::AsCollection;
use differential_dataflow::operators::arrange::{ArrangeByKey, Arrange};
use differential_dataflow::operators::group::GroupArranged;
use differential_dataflow::trace::implementations::ord::OrdValSpine;
use differential_dataflow::trace::{Trace, TraceReader};
use differential_dataflow::hashable::{OrdWrapper, UnsignedWrapper};
use itertools::Itertools;

type Result = std::sync::mpsc::Receiver<timely::dataflow::operators::capture::Event<timely::progress::nested::product::Product<timely::progress::timestamp::RootTimestamp, usize>, ((u64, i64), timely::progress::nested::product::Product<timely::progress::timestamp::RootTimestamp, usize>, i64)>>;

fn run_test<T>(test: T, expected: Vec<(Product<RootTimestamp, usize>, Vec<((u64, i64), i64)>)>) -> ()
        where T: FnOnce(Vec<Vec<((u64, u64), i64)>>)-> Result + ::std::panic::UnwindSafe
{
    let input_epochs: Vec<Vec<((u64, u64), i64)>> = vec![
        vec![((2, 0), 1), ((1, 0), 1), ((1, 3), 1), ((4, 2), 1)],
        vec![((2, 0), -1), ((1, 0), -1)],
        vec![((2, 0), 1), ((1, 3), -1)],
        vec![((1, 0), 1), ((1, 3), 1), ((2, 0), -1), ((4, 2), -1)],
        vec![((2, 0), 1), ((4, 2), 1), ((1, 3), -1)],
        vec![((1, 3), 1), ((4, 2), -1)],
    ];
    let captured = (test)(input_epochs);
    let mut results = captured.extract().into_iter().flat_map(|(_, data)| data).collect::<Vec<_>>();
    results.sort_by_key(|&(_, t, _)| t);
    let out =
    results
        .into_iter()
        .group_by(|&(_, t, _)| t)
        .into_iter()
        .map(|(t, vals)| {
            let mut vec = vals.map(|(v, _, w)| (v, w)).collect::<Vec<_>>();
            vec.sort();
            (t, vec)
        }).collect::<Vec<_>>();
    // println!("out: {:?}", out);
    assert_eq!(out, expected);
}

#[test]
fn test_import() {
    run_test(|input_epochs| {
        timely::execute(timely::Configuration::Process(4), move |worker| {
            let ref input_epochs = input_epochs;
            let index = worker.index();
            let peers = worker.peers();

            let (mut input, mut trace) = worker.dataflow(|scope| {
                let (input, edges) = scope.new_input();
                let arranged = edges.as_collection()
                                    .arrange_by_key_hashed();
                (input, arranged.trace.clone())
            });
            let (captured,) = worker.dataflow(move |scope| {
                let imported = trace.import(scope);
                ::std::mem::drop(trace);
                let captured = imported.group_arranged::<_, i64, _, _>(|_k, s, t| t.push((s.iter().map(|&(_, w)| w).sum(), 1i64)), OrdValSpine::new())
                      .as_collection(|k: &OrdWrapper<u64>, c: &i64| (k.item.clone(), *c))
                      .inner.exchange(|_| 0)
                      .capture();
                (captured,)
            });

            for (t, changes) in input_epochs.into_iter().enumerate() {
                if t != input.time().inner {
                    input.advance_to(t);
                }
                let &time = input.time();
                for &((src, dst), w) in changes.into_iter().filter(|&&((src, _), _)| (src as usize) % peers == index) {
                    input.send(((src, dst), time, w));
                }
            }
            input.close();

            captured
        }).unwrap().join().into_iter().map(|x| x.unwrap()).next().unwrap()
    }, vec![
        (RootTimestamp::new(0), vec![
             ((1, 2), 1), ((2, 1), 1), ((4, 1), 1)]),
        (RootTimestamp::new(1), vec![
             ((1, 1), 1), ((1, 2), -1), ((2, 1), -1)]),
        (RootTimestamp::new(2), vec![
             ((1, 1), -1), ((2, 1), 1)]),
        (RootTimestamp::new(3), vec![
             ((1, 2), 1), ((2, 1), -1), ((4, 1), -1)]),
        (RootTimestamp::new(4), vec![
             ((1, 1), 1), ((1, 2), -1), ((2, 1), 1), ((4, 1), 1)]),
        (RootTimestamp::new(5), vec![
             ((1, 1), -1), ((1, 2), 1), ((4, 1), -1)]),
    ]);
}

#[test]
fn test_import_completed_dataflow() {
    // Runs the first dataflow to completion before constructing the subscriber.
    run_test(|input_epochs| {
        timely::execute(timely::Configuration::Process(4), move |worker| {
            let ref input_epochs = input_epochs;
            let index = worker.index();
            let peers = worker.peers();

            let (mut input, mut trace, probe) = worker.dataflow(|scope| {
                let (input, edges) = scope.new_input();
                let arranged = edges.as_collection()
                                    .arrange_by_key_hashed();
                (input, arranged.trace.clone(), arranged.stream.probe())
            });

            for (t, changes) in input_epochs.into_iter().enumerate() {
                if t != input.time().inner {
                    input.advance_to(t);
                }
                let &time = input.time();
                for &((src, dst), w) in changes.into_iter().filter(|&&((src, _), _)| (src as usize) % peers == index) {
                    input.send(((src, dst), time, w));
                }
            }
            input.close();

            worker.step_while(|| !probe.done());

            let (_probe2, captured,) = worker.dataflow(move |scope| {
                let imported = trace.import(scope);
                ::std::mem::drop(trace);
                let stream = imported.group_arranged::<_, i64, _, _>(|_k, s, t| t.push((s.iter().map(|&(_, w)| w).sum(), 1i64)), OrdValSpine::new())
                      .as_collection(|k: &OrdWrapper<u64>, c: &i64| (k.item.clone(), *c))
                      .inner.exchange(|_| 0);
                let probe = stream.probe();
                let captured = stream.capture();
                (probe, captured,)
            });

            // println!("probe2: {}", probe2.with_frontier(|f| format!("{:?}", f)));

            captured
        }).unwrap().join().into_iter().map(|x| x.unwrap()).next().unwrap()
    }, vec![
        (RootTimestamp::new(0), vec![
             ((1, 2), 1), ((2, 1), 1), ((4, 1), 1)]),
        (RootTimestamp::new(1), vec![
             ((1, 1), 1), ((1, 2), -1), ((2, 1), -1)]),
        (RootTimestamp::new(2), vec![
             ((1, 1), -1), ((2, 1), 1)]),
        (RootTimestamp::new(3), vec![
             ((1, 2), 1), ((2, 1), -1), ((4, 1), -1)]),
        (RootTimestamp::new(4), vec![
             ((1, 1), 1), ((1, 2), -1), ((2, 1), 1), ((4, 1), 1)]),
        (RootTimestamp::new(5), vec![
             ((1, 1), -1), ((1, 2), 1), ((4, 1), -1)]),
    ]);
}

#[ignore]
#[test]
fn import_skewed() {

    run_test(|_input| {
        let captured = timely::execute(timely::Configuration::Process(4), |worker| {
            let index = worker.index();
            let peers = worker.peers();

            let (mut input, mut trace) = worker.dataflow(|scope| {
                let (input, edges) = scope.new_input();
                let arranged = edges.as_collection()
                                    .map(|(k,v)| (UnsignedWrapper::from(k), v))
                                    .arrange(OrdValSpine::new());
                (input, arranged.trace.clone())
            });

            input.send(((index as u64, 1), RootTimestamp::new(index), 1));
            input.close();

            trace.advance_by(&[RootTimestamp::new(peers - index)]);

            let (captured,) = worker.dataflow(move |scope| {
                let imported = trace.import(scope);
                let captured = imported
                      .as_collection(|k: &UnsignedWrapper<u64>, c: &i64| (k.item.clone(), *c))
                      .inner.exchange(|_| 0)
                      .capture();
                (captured,)
            });

            captured
        }).unwrap().join().into_iter().map(|x| x.unwrap()).next().unwrap();

        // Worker `index` sent `index` at time `index`, but advanced its handle to `peers - index`.
        // As its data should be shuffled back to it (we used an UnsignedWrapper) this means that
        // `index` should be present at time `max(index, peers-index)`.

        captured
    }, vec![
        (RootTimestamp::new(2), vec![((2, 1), 1)]),
        (RootTimestamp::new(3), vec![((1, 1), 1), ((3, 1), 1)]),
        (RootTimestamp::new(4), vec![((0, 1), 1)]),
    ]);
}
<|endoftext|>


<commit_before>extern crate libc;
use super::{Width, Height};
use std::os::raw::*;

#[cfg(not(target_os = "macos"))]
const TIOCGWINSZ: c_int = 0x00005413;
#[cfg(target_os = "macos")]
const TIOCGWINSZ: c_ulong = 1074295912;

#[derive(Debug)]
struct WinSize {
    ws_row: c_ushort,
    ws_col: c_ushort,
    ws_xpixel: c_ushort,
    ws_ypixel: c_ushort
}

/// Returns the size of the terminal, if available.
///
/// If STDOUT is not a tty, returns `None`
pub fn terminal_size() -> Option<(Width, Height)> {
    use self::libc::{isatty, STDOUT_FILENO};
    use self::libc::funcs::bsd44::ioctl;
    let is_tty: bool = unsafe{isatty(STDOUT_FILENO) == 1};

    if !is_tty {
        return None;
    }

    let (rows, cols) = unsafe {
        let mut winsize = WinSize{ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0};
        ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut winsize);
        let rows = if winsize.ws_row > 0 { winsize.ws_row } else { 0 };
        let cols = if winsize.ws_col > 0 { winsize.ws_col } else { 0 };
        (rows as u16, cols as u16)
    };

    if rows > 0 && cols > 0 {
        Some((Width(cols), Height(rows)))
    } else {
        None
    }
}
<commit_msg>test(tty/unix): fix test on mac<commit_after>extern crate libc;
use super::{Width, Height};
use std::os::raw::*;

#[cfg(not(target_os = "macos"))]
const TIOCGWINSZ: c_int = 0x00005413;
#[cfg(target_os = "macos")]
const TIOCGWINSZ: c_ulong = 1074295912;

#[derive(Debug)]
struct WinSize {
    ws_row: c_ushort,
    ws_col: c_ushort,
    ws_xpixel: c_ushort,
    ws_ypixel: c_ushort
}

/// Returns the size of the terminal, if available.
///
/// If STDOUT is not a tty, returns `None`
pub fn terminal_size() -> Option<(Width, Height)> {
    use self::libc::{isatty, STDOUT_FILENO};
    use self::libc::funcs::bsd44::ioctl;
    let is_tty: bool = unsafe{isatty(STDOUT_FILENO) == 1};

    if !is_tty {
        return None;
    }

    let (rows, cols) = unsafe {
        let mut winsize = WinSize{ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0};
        ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut winsize);
        let rows = if winsize.ws_row > 0 { winsize.ws_row } else { 0 };
        let cols = if winsize.ws_col > 0 { winsize.ws_col } else { 0 };
        (rows as u16, cols as u16)
    };

    if rows > 0 && cols > 0 {
        Some((Width(cols), Height(rows)))
    } else {
        None
    }
}

#[test]
/// Compare with the output of `stty size`
fn compare_with_stty() {
    use std::process::Command;
    use std::process::Stdio;
    let mut args = vec!["-F", "/dev/stderr", "size"];
    if cfg!(target_os = "macos") {
        args[0] = "-f"
    }
    let output = Command::new("stty")
                         .args(&args)
                         .stderr(Stdio::inherit()).output().unwrap();
    let stdout = String::from_utf8(output.stdout).unwrap();
     assert!(output.status.success());

    // stdout is "rows cols"
    let mut data = stdout.split_whitespace();
    let rows = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
    let cols = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
    println!("{}", stdout);
    println!("{} {}", rows, cols);

    if let Some((Width(w), Height(h))) = terminal_size() {
        assert_eq!(rows, h);
        assert_eq!(cols, w);
    }
}
<|endoftext|>