<commit_before>

use {Printer, With, XY};
use direction::Direction;
use event::{Event, EventResult, Key};
use odds::vec::VecExt;
use theme::{ColorStyle, Effect};
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use utils::{LinesIterator, Row, prefix};
use vec::Vec2;
use view::{ScrollBase, SizeCache, View};

/// Multi-lines text editor.
///
/// A `TextArea` will attempt to grow vertically and horizontally
/// dependent on the content.  Wrap it in a `BoxView` to
/// constrain its size.
pub struct TextArea {
    // TODO: use a smarter data structure (rope?)
    content: String,
    /// Byte offsets within `content` representing text rows
    rows: Vec<Row>,

    /// When `false`, we don't take any input.
    enabled: bool,

    /// Base for scrolling features
    scrollbase: ScrollBase,

    /// Cache to avoid re-computing layout on no-op events
    last_size: Option<XY<SizeCache>>,

    /// Byte offset of the currently selected grapheme.
    cursor: usize,
}

fn make_rows(text: &str, width: usize) -> Vec<Row> {
    LinesIterator::new(text, width)
        .show_spaces()
        .collect()
}

new_default!(TextArea);

impl TextArea {
    /// Creates a new, empty TextArea.
    pub fn new() -> Self {
        TextArea {
            content: String::new(),
            rows: Vec::new(),
            enabled: true,
            scrollbase: ScrollBase::new().right_padding(0),
            last_size: None,
            cursor: 0,
        }
    }

    /// Retrieves the content of the view.
    pub fn get_content(&self) -> &str {
        &self.content
    }

    fn invalidate(&mut self) {
        self.last_size = None;
    }

    /// Sets the content of the view.
    pub fn set_content<S: Into<String>>(&mut self, content: S) {
        self.content = content.into();
        if let Some(size) = self.last_size.map(|s| s.map(|s| s.value)) {
            self.compute_rows(size);
        }
    }

    /// Sets the content of the view.
    ///
    /// Chainable variant.
    pub fn content<S: Into<String>>(self, content: S) -> Self {
        self.with(|s| s.set_content(content))
    }

    /// Finds the row containing the grapheme at the given offset
    fn row_at(&self, offset: usize) -> usize {
        // println_stderr!("Offset: {}", offset);
        self.rows
            .iter()
            .enumerate()
            .take_while(|&(_, row)| row.start <= offset)
            .map(|(i, _)| i)
            .last()
            .unwrap()
    }

    fn col_at(&self, offset: usize) -> usize {
        let row_id = self.row_at(offset);
        let row = self.rows[row_id];
        // Number of cells to the left of the cursor
        self.content[row.start..offset].width()
    }

    /// Finds the row containing the cursor
    fn selected_row(&self) -> usize {
        self.row_at(self.cursor)
    }

    fn page_up(&mut self) {
        for _ in 0..5 {
            self.move_up();
        }
    }

    fn page_down(&mut self) {
        for _ in 0..5 {
            self.move_down();
        }
    }

    fn move_up(&mut self) {
        let row_id = self.selected_row();
        if row_id == 0 {
            return;
        }

        // Number of cells to the left of the cursor
        let x = self.col_at(self.cursor);

        let prev_row = self.rows[row_id - 1];
        let prev_text = &self.content[prev_row.start..prev_row.end];
        let offset = prefix(prev_text.graphemes(true), x, "").length;
        self.cursor = prev_row.start + offset;
    }

    fn move_down(&mut self) {
        let row_id = self.selected_row();
        if row_id + 1 == self.rows.len() {
            return;
        }
        let x = self.col_at(self.cursor);

        let next_row = self.rows[row_id + 1];
        let next_text = &self.content[next_row.start..next_row.end];
        let offset = prefix(next_text.graphemes(true), x, "").length;
        self.cursor = next_row.start + offset;
    }

    /// Moves the cursor to the left.
    ///
    /// Wraps the previous line if required.
    fn move_left(&mut self) {
        let len = {
            // We don't want to utf8-parse the entire content.
            // So restrict to the last row.
            let mut row = self.selected_row();
            if self.rows[row].start == self.cursor {
                row -= 1;
            }

            let text = &self.content[self.rows[row].start..self.cursor];
            text.graphemes(true)
                .last()
                .unwrap()
                .len()
        };
        self.cursor -= len;
    }

    /// Moves the cursor to the right.
    ///
    /// Jumps to the next line is required.
    fn move_right(&mut self) {
        let len = self.content[self.cursor..]
            .graphemes(true)
            .next()
            .unwrap()
            .len();
        self.cursor += len;
    }

    fn is_cache_valid(&self, size: Vec2) -> bool {
        match self.last_size {
            None => false,
            Some(ref last) => last.x.accept(size.x) && last.y.accept(size.y),
        }
    }

    fn fix_ghost_row(&mut self) {
        if self.rows.is_empty() ||
           self.rows.last().unwrap().end != self.content.len() {
            // Add a fake, empty row at the end.
            self.rows.push(Row {
                start: self.content.len(),
                end: self.content.len(),
                width: 0,
            });
        }
    }

    fn compute_rows(&mut self, size: Vec2) {
        if self.is_cache_valid(size) {
            return;
        }
        // println_stderr!("Computing! Oh yeah!");

        let mut available = size.x;
        self.rows = make_rows(&self.content, available);
        self.fix_ghost_row();
        if self.rows.len() > size.y {
            available -= 1;
            // Doh :(
            self.rows = make_rows(&self.content, available);
            self.fix_ghost_row();
        }

        if !self.rows.is_empty() {
            self.last_size = Some(SizeCache::build(size, size));
        }
        self.scrollbase.set_heights(size.y, self.rows.len());
    }

    fn backspace(&mut self) {
        if self.cursor != 0 {
            self.move_left();
            self.delete();
        }
    }

    fn delete(&mut self) {
        if self.cursor == self.content.len() {
            return;
        }
        // println_stderr!("Rows: {:?}", self.rows);
        let len = self.content[self.cursor..]
            .graphemes(true)
            .next()
            .unwrap()
            .len();
        let start = self.cursor;
        let end = self.cursor + len;
        // println_stderr!("Start/end: {}/{}", start, end);
        // println_stderr!("Content: `{}`", self.content);
        for _ in self.content.drain(start..end) {}
        // println_stderr!("Content: `{}`", self.content);

        let selected_row = self.selected_row();
        // println_stderr!("Selected row: {}", selected_row);
        if self.cursor == self.rows[selected_row].end {
            // We're removing an (implicit) newline.
            // This means merging two rows.
            let new_end = self.rows[selected_row + 1].end;
            self.rows[selected_row].end = new_end;
            self.rows.remove(selected_row + 1);
        }
        self.rows[selected_row].end -= len;

        // update all the rows downstream
        for row in &mut self.rows.iter_mut().skip(1 + selected_row) {
            row.rev_shift(len);
        }
        // println_stderr!("Rows: {:?}", self.rows);

        self.fix_damages();
        // println_stderr!("Rows: {:?}", self.rows);
    }

    fn insert(&mut self, ch: char) {

        // First, we inject the data, but keep the cursor unmoved
        // (So the cursor is to the left of the injected char)
        self.content.insert(self.cursor, ch);

        // Then, we shift the indexes of every row after this one.
        let shift = ch.len_utf8();

        // The current row grows, every other is just shifted.
        let selected_row = self.selected_row();
        self.rows[selected_row].end += shift;

        for row in &mut self.rows.iter_mut().skip(1 + selected_row) {
            row.shift(shift);
        }
        self.cursor += shift;

        // Finally, rows may not have the correct width anymore, so fix them.
        self.fix_damages();
    }

    /// Fix a damage located at the cursor.
    ///
    /// The only damages are assumed to have occured around the cursor.
    fn fix_damages(&mut self) {
        if self.last_size.is_none() {
            // If we don't know our size, it means we'll get a layout command soon.
            // So no need to do that here.
            return;
        }

        let size = self.last_size.unwrap().map(|s| s.value);

        // Find affected text.
        // We know the damage started at this row, so it'll need to go.
        let mut first_row = self.selected_row();
        // Actually, if possible, also re-compute the previous row.
        // Indeed, the previous row may have been cut short, and if we now
        // break apart a big word, maybe the first half can go up one level.
        if first_row > 0 {
            first_row -= 1;
        }
        // The
        let first_byte = self.rows[first_row].start;

        // We don't need to go beyond a newline.
        // If we don't find one, end of the text it is.
        // println_stderr!("Cursor: {}", self.cursor);
        let last_byte = self.content[self.cursor..]
            .find('\n')
            .map(|i| 1 + i + self.cursor);
        let last_row = last_byte.map_or(self.rows.len(), |last_byte| self.row_at(last_byte));
        let last_byte = last_byte.unwrap_or_else(|| self.content.len());

        // println_stderr!("Content: `{}` (len={})",
        //                 self.content,
        //                 self.content.len());
        // println_stderr!("start/end: {}/{}", first_byte, last_byte);
        // println_stderr!("start/end rows: {}/{}", first_row, last_row);

        // Do we have access to the entire width?...
        let mut available = size.x;

        let scrollable = self.rows.len() > size.y;
        if scrollable {
            // ... not if a scrollbar is there
            available -= 1;
        }

        // First attempt, if scrollbase status didn't change.
        // println_stderr!("Rows: {:?}", self.rows);
        let new_rows = make_rows(&self.content[first_byte..last_byte],
                                 available);
        // How much did this add?
        // println_stderr!("New rows: {:?}", new_rows);
        // println_stderr!("{}-{}", first_row, last_row);
        let new_row_count = self.rows.len() + new_rows.len() + first_row -
                            last_row;
        if !scrollable && new_row_count > size.y {
            // We just changed scrollable status.
            // This changes everything.
            // TODO: compute_rows() currently makes a scroll-less attempt.
            // Here, we know it's just no gonna happen.
            self.invalidate();
            self.compute_rows(size);
            return;
        }

        // Otherwise, replace stuff.
        let affected_rows = first_row..last_row;
        let replacement_rows = new_rows.into_iter()
            .map(|row| row.shifted(first_byte));
        self.rows.splice(affected_rows, replacement_rows);
        self.fix_ghost_row();
        self.scrollbase.set_heights(size.y, self.rows.len());
    }
}

impl View for TextArea {
    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
        self.compute_rows(constraint);
        Vec2::new(
            1 + self.rows.iter().map(|r| r.width).max().unwrap_or(1),
            self.rows.len()
        )
    }

    fn draw(&self, printer: &Printer) {
        printer.with_color(ColorStyle::Secondary, |printer| {
            let effect = if self.enabled {
                Effect::Reverse
            } else {
                Effect::Simple
            };

            let w = if self.scrollbase.scrollable() {
                printer.size.x - 1
            } else {
                printer.size.x
            };
            printer.with_effect(effect,
                                |printer| for y in 0..printer.size.y {
                                    printer.print_hline((0, y), w, " ");
                                });

            // println_stderr!("Content: `{}`", &self.content);
            self.scrollbase.draw(printer, |printer, i| {
                // println_stderr!("Drawing row {}", i);
                let row = &self.rows[i];
                // println_stderr!("row: {:?}", row);
                let text = &self.content[row.start..row.end];
                // println_stderr!("row text: `{}`", text);
                printer.with_effect(effect, |printer| {
                    printer.print((0, 0), text);
                });

                if printer.focused && i == self.selected_row() {
                    let cursor_offset = self.cursor - row.start;
                    let c = if cursor_offset == text.len() {
                        "_"
                    } else {
                        text[cursor_offset..]
                            .graphemes(true)
                            .next()
                            .expect("Found no char!")
                    };
                    let offset = text[..cursor_offset].width();
                    printer.print((offset, 0), c);
                }

            });
        });
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        match event {
            Event::Char(ch) => self.insert(ch),
            Event::Key(Key::Enter) => self.insert('\n'),
            Event::Key(Key::Backspace) if self.cursor > 0 => self.backspace(),
            Event::Key(Key::Del) if self.cursor < self.content.len() => {
                self.delete()
            }

            Event::Key(Key::End) => {
                let row = self.selected_row();
                self.cursor = self.rows[row].end;
                if row + 1 < self.rows.len() &&
                   self.cursor == self.rows[row + 1].start {
                    self.move_left();
                }
            }
            Event::Ctrl(Key::Home) => self.cursor = 0,
            Event::Ctrl(Key::End) => self.cursor = self.content.len(),
            Event::Key(Key::Home) => {
                self.cursor = self.rows[self.selected_row()].start
            }
            Event::Key(Key::Up) if self.selected_row() > 0 => self.move_up(),
            Event::Key(Key::Down) if self.selected_row() + 1 <
                                     self.rows.len() => self.move_down(),
            Event::Key(Key::PageUp) => self.page_up(),
            Event::Key(Key::PageDown) => self.page_down(),
            Event::Key(Key::Left) if self.cursor > 0 => self.move_left(),
            Event::Key(Key::Right) if self.cursor < self.content.len() => {
                self.move_right()
            }
            _ => return EventResult::Ignored,
        }

        // println_stderr!("Rows: {:?}", self.rows);
        let focus = self.selected_row();
        self.scrollbase.scroll_to(focus);

        EventResult::Consumed(None)
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.enabled
    }

    fn layout(&mut self, size: Vec2) {
        self.compute_rows(size);
    }
}
<commit_msg>Fix `VecExt::splice` conflict with `Vec::splice`<commit_after>

use {Printer, With, XY};
use direction::Direction;
use event::{Event, EventResult, Key};
use odds::vec::VecExt;
use theme::{ColorStyle, Effect};
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use utils::{LinesIterator, Row, prefix};
use vec::Vec2;
use view::{ScrollBase, SizeCache, View};

/// Multi-lines text editor.
///
/// A `TextArea` will attempt to grow vertically and horizontally
/// dependent on the content.  Wrap it in a `BoxView` to
/// constrain its size.
pub struct TextArea {
    // TODO: use a smarter data structure (rope?)
    content: String,
    /// Byte offsets within `content` representing text rows
    rows: Vec<Row>,

    /// When `false`, we don't take any input.
    enabled: bool,

    /// Base for scrolling features
    scrollbase: ScrollBase,

    /// Cache to avoid re-computing layout on no-op events
    last_size: Option<XY<SizeCache>>,

    /// Byte offset of the currently selected grapheme.
    cursor: usize,
}

fn make_rows(text: &str, width: usize) -> Vec<Row> {
    LinesIterator::new(text, width)
        .show_spaces()
        .collect()
}

new_default!(TextArea);

impl TextArea {
    /// Creates a new, empty TextArea.
    pub fn new() -> Self {
        TextArea {
            content: String::new(),
            rows: Vec::new(),
            enabled: true,
            scrollbase: ScrollBase::new().right_padding(0),
            last_size: None,
            cursor: 0,
        }
    }

    /// Retrieves the content of the view.
    pub fn get_content(&self) -> &str {
        &self.content
    }

    fn invalidate(&mut self) {
        self.last_size = None;
    }

    /// Sets the content of the view.
    pub fn set_content<S: Into<String>>(&mut self, content: S) {
        self.content = content.into();
        if let Some(size) = self.last_size.map(|s| s.map(|s| s.value)) {
            self.compute_rows(size);
        }
    }

    /// Sets the content of the view.
    ///
    /// Chainable variant.
    pub fn content<S: Into<String>>(self, content: S) -> Self {
        self.with(|s| s.set_content(content))
    }

    /// Finds the row containing the grapheme at the given offset
    fn row_at(&self, offset: usize) -> usize {
        // println_stderr!("Offset: {}", offset);
        self.rows
            .iter()
            .enumerate()
            .take_while(|&(_, row)| row.start <= offset)
            .map(|(i, _)| i)
            .last()
            .unwrap()
    }

    fn col_at(&self, offset: usize) -> usize {
        let row_id = self.row_at(offset);
        let row = self.rows[row_id];
        // Number of cells to the left of the cursor
        self.content[row.start..offset].width()
    }

    /// Finds the row containing the cursor
    fn selected_row(&self) -> usize {
        self.row_at(self.cursor)
    }

    fn page_up(&mut self) {
        for _ in 0..5 {
            self.move_up();
        }
    }

    fn page_down(&mut self) {
        for _ in 0..5 {
            self.move_down();
        }
    }

    fn move_up(&mut self) {
        let row_id = self.selected_row();
        if row_id == 0 {
            return;
        }

        // Number of cells to the left of the cursor
        let x = self.col_at(self.cursor);

        let prev_row = self.rows[row_id - 1];
        let prev_text = &self.content[prev_row.start..prev_row.end];
        let offset = prefix(prev_text.graphemes(true), x, "").length;
        self.cursor = prev_row.start + offset;
    }

    fn move_down(&mut self) {
        let row_id = self.selected_row();
        if row_id + 1 == self.rows.len() {
            return;
        }
        let x = self.col_at(self.cursor);

        let next_row = self.rows[row_id + 1];
        let next_text = &self.content[next_row.start..next_row.end];
        let offset = prefix(next_text.graphemes(true), x, "").length;
        self.cursor = next_row.start + offset;
    }

    /// Moves the cursor to the left.
    ///
    /// Wraps the previous line if required.
    fn move_left(&mut self) {
        let len = {
            // We don't want to utf8-parse the entire content.
            // So restrict to the last row.
            let mut row = self.selected_row();
            if self.rows[row].start == self.cursor {
                row -= 1;
            }

            let text = &self.content[self.rows[row].start..self.cursor];
            text.graphemes(true)
                .last()
                .unwrap()
                .len()
        };
        self.cursor -= len;
    }

    /// Moves the cursor to the right.
    ///
    /// Jumps to the next line is required.
    fn move_right(&mut self) {
        let len = self.content[self.cursor..]
            .graphemes(true)
            .next()
            .unwrap()
            .len();
        self.cursor += len;
    }

    fn is_cache_valid(&self, size: Vec2) -> bool {
        match self.last_size {
            None => false,
            Some(ref last) => last.x.accept(size.x) && last.y.accept(size.y),
        }
    }

    fn fix_ghost_row(&mut self) {
        if self.rows.is_empty() ||
           self.rows.last().unwrap().end != self.content.len() {
            // Add a fake, empty row at the end.
            self.rows.push(Row {
                start: self.content.len(),
                end: self.content.len(),
                width: 0,
            });
        }
    }

    fn compute_rows(&mut self, size: Vec2) {
        if self.is_cache_valid(size) {
            return;
        }
        // println_stderr!("Computing! Oh yeah!");

        let mut available = size.x;
        self.rows = make_rows(&self.content, available);
        self.fix_ghost_row();
        if self.rows.len() > size.y {
            available -= 1;
            // Doh :(
            self.rows = make_rows(&self.content, available);
            self.fix_ghost_row();
        }

        if !self.rows.is_empty() {
            self.last_size = Some(SizeCache::build(size, size));
        }
        self.scrollbase.set_heights(size.y, self.rows.len());
    }

    fn backspace(&mut self) {
        if self.cursor != 0 {
            self.move_left();
            self.delete();
        }
    }

    fn delete(&mut self) {
        if self.cursor == self.content.len() {
            return;
        }
        // println_stderr!("Rows: {:?}", self.rows);
        let len = self.content[self.cursor..]
            .graphemes(true)
            .next()
            .unwrap()
            .len();
        let start = self.cursor;
        let end = self.cursor + len;
        // println_stderr!("Start/end: {}/{}", start, end);
        // println_stderr!("Content: `{}`", self.content);
        for _ in self.content.drain(start..end) {}
        // println_stderr!("Content: `{}`", self.content);

        let selected_row = self.selected_row();
        // println_stderr!("Selected row: {}", selected_row);
        if self.cursor == self.rows[selected_row].end {
            // We're removing an (implicit) newline.
            // This means merging two rows.
            let new_end = self.rows[selected_row + 1].end;
            self.rows[selected_row].end = new_end;
            self.rows.remove(selected_row + 1);
        }
        self.rows[selected_row].end -= len;

        // update all the rows downstream
        for row in &mut self.rows.iter_mut().skip(1 + selected_row) {
            row.rev_shift(len);
        }
        // println_stderr!("Rows: {:?}", self.rows);

        self.fix_damages();
        // println_stderr!("Rows: {:?}", self.rows);
    }

    fn insert(&mut self, ch: char) {

        // First, we inject the data, but keep the cursor unmoved
        // (So the cursor is to the left of the injected char)
        self.content.insert(self.cursor, ch);

        // Then, we shift the indexes of every row after this one.
        let shift = ch.len_utf8();

        // The current row grows, every other is just shifted.
        let selected_row = self.selected_row();
        self.rows[selected_row].end += shift;

        for row in &mut self.rows.iter_mut().skip(1 + selected_row) {
            row.shift(shift);
        }
        self.cursor += shift;

        // Finally, rows may not have the correct width anymore, so fix them.
        self.fix_damages();
    }

    /// Fix a damage located at the cursor.
    ///
    /// The only damages are assumed to have occured around the cursor.
    fn fix_damages(&mut self) {
        if self.last_size.is_none() {
            // If we don't know our size, it means we'll get a layout command soon.
            // So no need to do that here.
            return;
        }

        let size = self.last_size.unwrap().map(|s| s.value);

        // Find affected text.
        // We know the damage started at this row, so it'll need to go.
        let mut first_row = self.selected_row();
        // Actually, if possible, also re-compute the previous row.
        // Indeed, the previous row may have been cut short, and if we now
        // break apart a big word, maybe the first half can go up one level.
        if first_row > 0 {
            first_row -= 1;
        }
        // The
        let first_byte = self.rows[first_row].start;

        // We don't need to go beyond a newline.
        // If we don't find one, end of the text it is.
        // println_stderr!("Cursor: {}", self.cursor);
        let last_byte = self.content[self.cursor..]
            .find('\n')
            .map(|i| 1 + i + self.cursor);
        let last_row = last_byte.map_or(self.rows.len(), |last_byte| self.row_at(last_byte));
        let last_byte = last_byte.unwrap_or_else(|| self.content.len());

        // println_stderr!("Content: `{}` (len={})",
        //                 self.content,
        //                 self.content.len());
        // println_stderr!("start/end: {}/{}", first_byte, last_byte);
        // println_stderr!("start/end rows: {}/{}", first_row, last_row);

        // Do we have access to the entire width?...
        let mut available = size.x;

        let scrollable = self.rows.len() > size.y;
        if scrollable {
            // ... not if a scrollbar is there
            available -= 1;
        }

        // First attempt, if scrollbase status didn't change.
        // println_stderr!("Rows: {:?}", self.rows);
        let new_rows = make_rows(&self.content[first_byte..last_byte],
                                 available);
        // How much did this add?
        // println_stderr!("New rows: {:?}", new_rows);
        // println_stderr!("{}-{}", first_row, last_row);
        let new_row_count = self.rows.len() + new_rows.len() + first_row -
                            last_row;
        if !scrollable && new_row_count > size.y {
            // We just changed scrollable status.
            // This changes everything.
            // TODO: compute_rows() currently makes a scroll-less attempt.
            // Here, we know it's just no gonna happen.
            self.invalidate();
            self.compute_rows(size);
            return;
        }

        // Otherwise, replace stuff.
        let affected_rows = first_row..last_row;
        let replacement_rows = new_rows.into_iter()
            .map(|row| row.shifted(first_byte));
        VecExt::splice(&mut self.rows, affected_rows, replacement_rows);
        self.fix_ghost_row();
        self.scrollbase.set_heights(size.y, self.rows.len());
    }
}

impl View for TextArea {
    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
        self.compute_rows(constraint);
        Vec2::new(
            1 + self.rows.iter().map(|r| r.width).max().unwrap_or(1),
            self.rows.len()
        )
    }

    fn draw(&self, printer: &Printer) {
        printer.with_color(ColorStyle::Secondary, |printer| {
            let effect = if self.enabled {
                Effect::Reverse
            } else {
                Effect::Simple
            };

            let w = if self.scrollbase.scrollable() {
                printer.size.x - 1
            } else {
                printer.size.x
            };
            printer.with_effect(effect,
                                |printer| for y in 0..printer.size.y {
                                    printer.print_hline((0, y), w, " ");
                                });

            // println_stderr!("Content: `{}`", &self.content);
            self.scrollbase.draw(printer, |printer, i| {
                // println_stderr!("Drawing row {}", i);
                let row = &self.rows[i];
                // println_stderr!("row: {:?}", row);
                let text = &self.content[row.start..row.end];
                // println_stderr!("row text: `{}`", text);
                printer.with_effect(effect, |printer| {
                    printer.print((0, 0), text);
                });

                if printer.focused && i == self.selected_row() {
                    let cursor_offset = self.cursor - row.start;
                    let c = if cursor_offset == text.len() {
                        "_"
                    } else {
                        text[cursor_offset..]
                            .graphemes(true)
                            .next()
                            .expect("Found no char!")
                    };
                    let offset = text[..cursor_offset].width();
                    printer.print((offset, 0), c);
                }

            });
        });
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        match event {
            Event::Char(ch) => self.insert(ch),
            Event::Key(Key::Enter) => self.insert('\n'),
            Event::Key(Key::Backspace) if self.cursor > 0 => self.backspace(),
            Event::Key(Key::Del) if self.cursor < self.content.len() => {
                self.delete()
            }

            Event::Key(Key::End) => {
                let row = self.selected_row();
                self.cursor = self.rows[row].end;
                if row + 1 < self.rows.len() &&
                   self.cursor == self.rows[row + 1].start {
                    self.move_left();
                }
            }
            Event::Ctrl(Key::Home) => self.cursor = 0,
            Event::Ctrl(Key::End) => self.cursor = self.content.len(),
            Event::Key(Key::Home) => {
                self.cursor = self.rows[self.selected_row()].start
            }
            Event::Key(Key::Up) if self.selected_row() > 0 => self.move_up(),
            Event::Key(Key::Down) if self.selected_row() + 1 <
                                     self.rows.len() => self.move_down(),
            Event::Key(Key::PageUp) => self.page_up(),
            Event::Key(Key::PageDown) => self.page_down(),
            Event::Key(Key::Left) if self.cursor > 0 => self.move_left(),
            Event::Key(Key::Right) if self.cursor < self.content.len() => {
                self.move_right()
            }
            _ => return EventResult::Ignored,
        }

        // println_stderr!("Rows: {:?}", self.rows);
        let focus = self.selected_row();
        self.scrollbase.scroll_to(focus);

        EventResult::Consumed(None)
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.enabled
    }

    fn layout(&mut self, size: Vec2) {
        self.compute_rows(size);
    }
}
<|endoftext|>


<commit_before>#![allow(non_camel_case_types)]

extern crate rand;
extern crate rayon;
extern crate time;

use rand::{Rng, SeedableRng, XorShiftRng};
use std::env;
use std::str::FromStr;

trait Joiner {
    fn is_parallel() -> bool;
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send;
}

struct Parallel;
impl Joiner for Parallel {
    #[inline]
    fn is_parallel() -> bool {
        true
    }
    #[inline]
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send
    {
        rayon::join(oper_a, oper_b)
    }
}

struct Sequential;
impl Joiner for Sequential {
    #[inline]
    fn is_parallel() -> bool {
        false
    }
    #[inline]
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send
    {
        let a = oper_a();
        let b = oper_b();
        (a, b)
    }
}

fn quick_sort<J:Joiner, T:PartialOrd+Send>(v: &mut [T]) {
    if v.len() <= 1 {
        return;
    }

    if J::is_parallel() && v.len() <= 5*1024 {
        return quick_sort::<Sequential, T>(v);
    }

    let mid = partition(v);
    let (lo, hi) = v.split_at_mut(mid);
    J::join(|| quick_sort::<J,T>(lo),
            || quick_sort::<J,T>(hi));
}

fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
    let pivot = v.len() - 1;
    let mut i = 0;
    for j in 0..pivot {
        if v[j] <= v[pivot] {
            v.swap(i, j);
            i += 1;
        }
    }
    v.swap(i, pivot);
    i
}

fn main() {
    let mut kilo = 24;
    let mut run_par = true;
    let mut run_seq = true;
    let mut reps = 10;

    let mut args = env::args().skip(1);
    while let Some(arg) = args.next() {
        if arg == "--no-par" {
            run_par = false;
        } else if arg == "--no-seq" {
            run_seq = false;
        } else if arg == "--reps" {
            if let Some(reps_arg) = args.next() {
                match usize::from_str(&reps_arg) {
                    Ok(v) => reps = v,
                    Err(_) => {
                        println!("invalid argument `{}`, expected integer", reps_arg);
                    }
                }
            } else {
                println!("missing argument to --reps");
            }
        } else {
            match usize::from_str(&arg) {
                Ok(v) => kilo = v,
                Err(_) => {
                    println!("invalid argument `{}`, expected integer", arg);
                }
            }
        }
    }

    let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
    let mut par_time = 0;
    let mut seq_time = 0;

    rayon::initialize();

    for _ in 0 .. reps {
        let data: Vec<_> = (0..kilo*1024).map(|_| rng.next_u32()).collect();

        if run_par {
            let mut data = data.clone();
            let par_start = time::precise_time_ns();
            quick_sort::<Parallel, u32>(&mut data);
            par_time += time::precise_time_ns() - par_start;
        }

        if run_seq {
            let mut data = data.clone();
            let seq_start = time::precise_time_ns();
            quick_sort::<Sequential, u32>(&mut data);
            seq_time += time::precise_time_ns() - seq_start;
        }
    }

    println!("Array length    : {}K 32-bit integers", kilo);
    println!("Repetitions     : {}", reps);
    if run_par { println!("Parallel time   : {}", par_time); }
    if run_seq { println!("Sequential time : {}", seq_time); }
    if run_par && run_seq {
        println!("Parallel speedup: {}", (seq_time as f64) / (par_time as f64));
    }
}
<commit_msg>Fix quicksort demo. Fixes #5.<commit_after>#![allow(non_camel_case_types)]

extern crate rand;
extern crate rayon;
extern crate time;

use rand::{Rng, SeedableRng, XorShiftRng};
use std::env;
use std::str::FromStr;

trait Joiner {
    fn is_parallel() -> bool;
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send,
              R_A: Send, R_B: Send;
}

struct Parallel;
impl Joiner for Parallel {
    #[inline]
    fn is_parallel() -> bool {
        true
    }
    #[inline]
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send,
              R_A: Send, R_B: Send
    {
        rayon::join(oper_a, oper_b)
    }
}

struct Sequential;
impl Joiner for Sequential {
    #[inline]
    fn is_parallel() -> bool {
        false
    }
    #[inline]
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send,
              R_A: Send, R_B: Send
    {
        let a = oper_a();
        let b = oper_b();
        (a, b)
    }
}

fn quick_sort<J:Joiner, T:PartialOrd+Send>(v: &mut [T]) {
    if v.len() <= 1 {
        return;
    }

    if J::is_parallel() && v.len() <= 5*1024 {
        return quick_sort::<Sequential, T>(v);
    }

    let mid = partition(v);
    let (lo, hi) = v.split_at_mut(mid);
    J::join(|| quick_sort::<J,T>(lo),
            || quick_sort::<J,T>(hi));
}

fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
    let pivot = v.len() - 1;
    let mut i = 0;
    for j in 0..pivot {
        if v[j] <= v[pivot] {
            v.swap(i, j);
            i += 1;
        }
    }
    v.swap(i, pivot);
    i
}

fn main() {
    let mut kilo = 24;
    let mut run_par = true;
    let mut run_seq = true;
    let mut reps = 10;

    let mut args = env::args().skip(1);
    while let Some(arg) = args.next() {
        if arg == "--no-par" {
            run_par = false;
        } else if arg == "--no-seq" {
            run_seq = false;
        } else if arg == "--reps" {
            if let Some(reps_arg) = args.next() {
                match usize::from_str(&reps_arg) {
                    Ok(v) => reps = v,
                    Err(_) => {
                        println!("invalid argument `{}`, expected integer", reps_arg);
                    }
                }
            } else {
                println!("missing argument to --reps");
            }
        } else {
            match usize::from_str(&arg) {
                Ok(v) => kilo = v,
                Err(_) => {
                    println!("invalid argument `{}`, expected integer", arg);
                }
            }
        }
    }

    let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
    let mut par_time = 0;
    let mut seq_time = 0;

    rayon::initialize();

    for _ in 0 .. reps {
        let data: Vec<_> = (0..kilo*1024).map(|_| rng.next_u32()).collect();

        if run_par {
            let mut data = data.clone();
            let par_start = time::precise_time_ns();
            quick_sort::<Parallel, u32>(&mut data);
            par_time += time::precise_time_ns() - par_start;
        }

        if run_seq {
            let mut data = data.clone();
            let seq_start = time::precise_time_ns();
            quick_sort::<Sequential, u32>(&mut data);
            seq_time += time::precise_time_ns() - seq_start;
        }
    }

    println!("Array length    : {}K 32-bit integers", kilo);
    println!("Repetitions     : {}", reps);
    if run_par { println!("Parallel time   : {}", par_time); }
    if run_seq { println!("Sequential time : {}", seq_time); }
    if run_par && run_seq {
        println!("Parallel speedup: {}", (seq_time as f64) / (par_time as f64));
    }
}
<|endoftext|>


<commit_before>use std::process::{Command, exit};

fn main() {
    // Build the command to run.
    let mut process = Command::new("elm");
    process
        .arg("make")
        .arg("Main.elm")
        .arg("--warn").arg("--yes")
        .arg("--output").arg("../static/elm.js")
        .current_dir("site");

    // Try to run the command.
    let status = process.status().unwrap_or_else(|e| {
        println!("Error running process: {}", e);
        println!("You may need to install the elm tools.");
        exit(1);
    });

    // Fail if the build failed.
    if !status.success() {
        println!("Build failed");
        exit(1);
    }
}

<commit_msg>ci: Retry `elm make` several times in case of network failure<commit_after>use std::convert::From;
use std::error;
use std::error::Error as StdError;
use std::fmt;
use std::fs;
use std::io;
use std::process::{Command, exit};

use ElmMakeError::{CouldNotRun, Failed};

#[derive(Debug)]
enum ElmMakeError {
    CouldNotRun(io::Error),
    Failed,
}

impl fmt::Display for ElmMakeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let desc = self.description();
        match self {
            &CouldNotRun(ref e) =>
                write!(f, "{}: {}\nAre the elm tools installed?", desc, e),
            &Failed => write!(f, "{}", desc),
        }
    }
}

impl error::Error for ElmMakeError {
    fn description(&self) -> &str {
        match self {
            &CouldNotRun(_) => "Could not run `elm make`",
            &Failed => "elm-make build failed",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &CouldNotRun(ref e) => Some(e),
            &Failed => None,
        }
    }
}

impl From<io::Error> for ElmMakeError {
    fn from(err: io::Error) -> ElmMakeError {
        CouldNotRun(err)
    }
}

fn elm_make_once() -> Result<(), ElmMakeError> {
    // Build the command to run.
    let mut process = Command::new("elm");
    process
        .arg("make")
        .arg("Main.elm")
        .arg("--warn").arg("--yes")
        .arg("--output").arg("../static/elm.js")
        .current_dir("site");

    // Try to run the command.
    let status = try!(process.status());
    if !status.success() {
        return Err(Failed);
    }
    Ok(())
}

fn elm_make() -> Result<(), ElmMakeError> {
    if fs::metadata("site/elm-stuff").is_err() {
        println!("Will retry `elm make` if it fails.");
        for i in 0..2 {
            match elm_make_once() {
                Err(Failed) => {
                    println!("Build {} failed, perhaps because of network.", i);
                    println!("Retrying...");
                    continue
                }
                result => return result,
            }
        }
    }
    elm_make_once()
}

fn main() {
    match elm_make() {
        Ok(()) => {}
        Err(ref err) => {
            println!("{}", err);
            exit(1);
        }
    }
}

<|endoftext|>


<commit_before>use libc::c_ulonglong;

#[doc(hidden)]
pub fn marshal<T, F>(buf: &[u8],
                     padbefore: usize,
                     bytestodrop: usize,
                     f: F
                     ) -> (Vec<u8>, T)
    where F: Fn(*mut u8, *const u8, c_ulonglong) -> T {
    let mut dst = Vec::with_capacity(buf.len() + padbefore);
    for _ in range(0, padbefore) {
        dst.push(0);
    }
    dst.push_all(buf);
    let pdst = dst.as_mut_ptr();
    let psrc = dst.as_ptr();
    let res = f(pdst, psrc, dst.len() as c_ulonglong);
    (dst.into_iter().skip(bytestodrop).collect(), res)
}

macro_rules! newtype_clone (($newtype:ident) => (
        impl Clone for $newtype {
            fn clone(&self) -> $newtype {
                let &$newtype(v) = self;
                $newtype(v)
            }
        }

        ));

macro_rules! newtype_drop (($newtype:ident) => (
        impl Drop for $newtype {
            fn drop(&mut self) {
                let &$newtype(ref mut v) = self;
                unsafe {
                    volatile_set_memory(v.as_mut_ptr(), 0, v.len());
                }
            }
        }
        ));

macro_rules! newtype_impl (($newtype:ident, $len:expr) => (
    impl $newtype {
        /**
         * `from_slice()` creates an object from a byte slice
         *
         * This function will fail and return None if the length of
         * the byte-slice isn't equal to the length of the object
         */
        pub fn from_slice(bs: &[u8]) -> Option<$newtype> {
            if bs.len() != $len {
                return None
            }
            let mut n = $newtype([0; $len]);
            {
                let $newtype(ref mut b) = n;
                for (bi, &bsi) in b.iter_mut().zip(bs.iter()) {
                    *bi = bsi
                }
            }
            Some(n)
        }
        /**
         * `as_slice()` returns a byte slice containing the object contents
         *
         * WARNING: it might be tempting to do comparisons on objects by
         * using `x.as_slice() == y.as_slice()`. This will open up for
         * timing attacks when comparing for example authenticator
         * tags. Because of this only use the comparison functions
         * exposed by the sodiumoxide API.
         */
        pub fn as_slice(&self) -> &[u8] {
            let &$newtype(ref bs) = self;
            bs.as_slice()
        }
        /**
         * `as_mut_slice()` returns a mutable byte slice containing the object
         * contents
         *
         * WARNING: it might be tempting to do comparisons on objects by
         * using `x.as_mut_slice() == y.as_mut_slice()`. This will
         * open up for timing attacks when comparing for example
         * authenticator tags. Because of this only use the comparison
         * functions exposed by the sodiumoxide API.
         */
        pub fn as_mut_slice(&mut self) -> &mut [u8] {
            let &$newtype(ref mut bs) = self;
            bs.as_mut_slice()
        }
    }
    ));
<commit_msg>Changed &Newtype(ref mut k) to &mut Newtype(ref mut k)<commit_after>use libc::c_ulonglong;

#[doc(hidden)]
pub fn marshal<T, F>(buf: &[u8],
                     padbefore: usize,
                     bytestodrop: usize,
                     f: F
                     ) -> (Vec<u8>, T)
    where F: Fn(*mut u8, *const u8, c_ulonglong) -> T {
    let mut dst = Vec::with_capacity(buf.len() + padbefore);
    for _ in range(0, padbefore) {
        dst.push(0);
    }
    dst.push_all(buf);
    let pdst = dst.as_mut_ptr();
    let psrc = dst.as_ptr();
    let res = f(pdst, psrc, dst.len() as c_ulonglong);
    (dst.into_iter().skip(bytestodrop).collect(), res)
}

macro_rules! newtype_clone (($newtype:ident) => (
        impl Clone for $newtype {
            fn clone(&self) -> $newtype {
                let &$newtype(v) = self;
                $newtype(v)
            }
        }

        ));

macro_rules! newtype_drop (($newtype:ident) => (
        impl Drop for $newtype {
            fn drop(&mut self) {
                let &mut $newtype(ref mut v) = self;
                unsafe {
                    volatile_set_memory(v.as_mut_ptr(), 0, v.len());
                }
            }
        }
        ));

macro_rules! newtype_impl (($newtype:ident, $len:expr) => (
    impl $newtype {
        /**
         * `from_slice()` creates an object from a byte slice
         *
         * This function will fail and return None if the length of
         * the byte-slice isn't equal to the length of the object
         */
        pub fn from_slice(bs: &[u8]) -> Option<$newtype> {
            if bs.len() != $len {
                return None
            }
            let mut n = $newtype([0; $len]);
            {
                let $newtype(ref mut b) = n;
                for (bi, &bsi) in b.iter_mut().zip(bs.iter()) {
                    *bi = bsi
                }
            }
            Some(n)
        }
        /**
         * `as_slice()` returns a byte slice containing the object contents
         *
         * WARNING: it might be tempting to do comparisons on objects by
         * using `x.as_slice() == y.as_slice()`. This will open up for
         * timing attacks when comparing for example authenticator
         * tags. Because of this only use the comparison functions
         * exposed by the sodiumoxide API.
         */
        pub fn as_slice(&self) -> &[u8] {
            let &$newtype(ref bs) = self;
            bs.as_slice()
        }
        /**
         * `as_mut_slice()` returns a mutable byte slice containing the object
         * contents
         *
         * WARNING: it might be tempting to do comparisons on objects by
         * using `x.as_mut_slice() == y.as_mut_slice()`. This will
         * open up for timing attacks when comparing for example
         * authenticator tags. Because of this only use the comparison
         * functions exposed by the sodiumoxide API.
         */
        pub fn as_mut_slice(&mut self) -> &mut [u8] {
            let &mut $newtype(ref mut bs) = self;
            bs.as_mut_slice()
        }
    }
    ));
<|endoftext|>


<commit_before>#![macro_use]

use std::iter::Peekable;

macro_rules! log_debug {
    ($fmt:expr) => (
        #[cfg(test)]
        println!($fmt));
    ($fmt:expr, $($arg:tt)*) => (
        #[cfg(test)]
        println!($fmt, $($arg)*));
}

/// Macro to create String -> T hash map from list of static string and values
macro_rules! string_hash_map {
    [$(($key: expr, $value: expr)), *] => ({
        let mut result = HashMap::new();
        $(
            result.insert($key.to_string(), $value)
         ); *;
        result
     })
}

/// Iterator to advance iterator until next value meets requirements
pub struct ExclusiveTakeWhile<'a, T, P>
where
    T: 'a,
    T: Iterator,
{
    iter: &'a mut Peekable<T>,
    pred: P,
}

impl<'a, T: Iterator, P> Iterator for ExclusiveTakeWhile<'a, T, P>
where
    P: FnMut(&T::Item) -> bool,
{
    type Item = T::Item;

    fn next(&mut self) -> Option<Self::Item> {
        if if let Some(val) = self.iter.peek() {
            (self.pred)(val)
        } else {
            false
        }
        {
            self.iter.next()
        } else {
            None
        }
    }
}

// Add function to Peekable
pub trait AsExclusiveTakeWhile<'a, T>
where
    T: Iterator,
{
    fn take_while_exclusive<P>(self, predicate: P) -> ExclusiveTakeWhile<'a, T, P>
    where
        P: FnMut(&T::Item) -> bool;
}

impl<'a, T> AsExclusiveTakeWhile<'a, T> for &'a mut Peekable<T>
where
    T: Iterator,
{
    fn take_while_exclusive<P>(self, predicate: P) -> ExclusiveTakeWhile<'a, T, P> {
        ExclusiveTakeWhile::<T, P> {
            iter: self,
            pred: predicate,
        }
    }
}
<commit_msg>Fix formatted hashmap macro<commit_after>#![macro_use]

use std::iter::Peekable;

macro_rules! log_debug {
    ($fmt:expr) => (
        #[cfg(test)]
        println!($fmt));
    ($fmt:expr, $($arg:tt)*) => (
        #[cfg(test)]
        println!($fmt, $($arg)*));
}

/// Macro to create String -> T hash map from list of static string and values
macro_rules! string_hash_map {
    [$(($key: expr, $value: expr)), *,] => ({
        let mut result = HashMap::new();
        $(
            result.insert($key.to_string(), $value)
         ); *;
        result
     })
}

/// Iterator to advance iterator until next value meets requirements
pub struct ExclusiveTakeWhile<'a, T, P>
where
    T: 'a,
    T: Iterator,
{
    iter: &'a mut Peekable<T>,
    pred: P,
}

impl<'a, T: Iterator, P> Iterator for ExclusiveTakeWhile<'a, T, P>
where
    P: FnMut(&T::Item) -> bool,
{
    type Item = T::Item;

    fn next(&mut self) -> Option<Self::Item> {
        if if let Some(val) = self.iter.peek() {
            (self.pred)(val)
        } else {
            false
        }
        {
            self.iter.next()
        } else {
            None
        }
    }
}

// Add function to Peekable
pub trait AsExclusiveTakeWhile<'a, T>
where
    T: Iterator,
{
    fn take_while_exclusive<P>(self, predicate: P) -> ExclusiveTakeWhile<'a, T, P>
    where
        P: FnMut(&T::Item) -> bool;
}

impl<'a, T> AsExclusiveTakeWhile<'a, T> for &'a mut Peekable<T>
where
    T: Iterator,
{
    fn take_while_exclusive<P>(self, predicate: P) -> ExclusiveTakeWhile<'a, T, P> {
        ExclusiveTakeWhile::<T, P> {
            iter: self,
            pred: predicate,
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::time::{Duration, Instant};

use regex::{Captures, Regex};

use console::{measure_text_width, Style};

pub fn duration_to_secs(d: Duration) -> f64 {
    d.as_secs() as f64 + f64::from(d.subsec_nanos()) / 1_000_000_000f64
}

pub fn secs_to_duration(s: f64) -> Duration {
    let secs = s.trunc() as u64;
    let nanos = (s.fract() * 1_000_000_000f64) as u32;
    Duration::new(secs, nanos)
}

pub struct Estimate {
    buf: Vec<f64>,
    buf_cap: usize,
    last_idx: usize,
    started: Option<(Instant, u64)>,
}

impl Estimate {
    pub fn new() -> Estimate {
        Estimate {
            buf: vec![],
            buf_cap: 10,
            last_idx: 0,
            started: None,
        }
    }

    pub fn reset(&mut self) {
        self.buf.clear();
        self.last_idx = 0;
        self.started = None;
    }

    pub fn record_step(&mut self, value: u64) {
        // record initial position
        let (started_time, started_value) = match self.started {
            None => {
                let rv = (Instant::now(), value);
                self.started = Some(rv);
                rv
            }
            Some(value) => value,
        };

        let item = if value == 0 {
            0.0
        } else {
            let divisor = value.saturating_sub(started_value) as f64;
            if divisor == 0.0 {
                0.0
            } else {
                duration_to_secs(started_time.elapsed()) / divisor
            }
        };
        if self.buf.len() >= self.buf_cap {
            let idx = self.last_idx % self.buf.len();
            self.buf[idx] = item;
        } else {
            self.buf.push(item);
        }
        self.last_idx += 1;
    }

    pub fn time_per_step(&self) -> Duration {
        if self.buf.is_empty() {
            Duration::new(0, 0)
        } else {
            secs_to_duration(self.buf.iter().sum::<f64>() / self.buf.len() as f64)
        }
    }
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Alignment {
    Left,
    Center,
    Right,
}

#[derive(Debug)]
pub struct TemplateVar<'a> {
    pub key: &'a str,
    pub align: Alignment,
    pub truncate: bool,
    pub width: Option<usize>,
    pub style: Option<Style>,
    pub alt_style: Option<Style>,
    pub last_element: bool,
}

impl<'a> TemplateVar<'a> {
    pub fn duplicate_for_key<'b>(&self, key: &'b str) -> TemplateVar<'b> {
        TemplateVar {
            key,
            align: self.align,
            truncate: self.truncate,
            width: self.width,
            style: self.style.clone(),
            alt_style: self.alt_style.clone(),
            last_element: self.last_element,
        }
    }
}

pub fn expand_template<F: Fn(&TemplateVar<'_>) -> String>(s: &str, f: F) -> Cow<'_, str> {
    lazy_static::lazy_static! {
        static ref VAR_RE: Regex = Regex::new(r"(\}\})|\{(\{|[^}]+\})").unwrap();
        static ref KEY_RE: Regex = Regex::new(
            r"(?x)
                ([^:]+)
                (?:
                    :
                    ([<^>])?
                    ([0-9]+)?
                    (!)?
                    (?:\.([a-z_]+(?:\.[a-z_]+)*))?
                    (?:/([a-z_]+(?:\.[a-z_]+)*))?
                )?
            "
        )
        .unwrap();
    }
    VAR_RE.replace_all(s, |caps: &Captures<'_>| {
        if caps.get(1).is_some() {
            return "}".into();
        }
        let key = &caps[2];
        if key == "{" {
            return "{".into();
        }
        let mut var = TemplateVar {
            key,
            align: Alignment::Left,
            truncate: false,
            width: None,
            style: None,
            alt_style: None,
            last_element: caps.get(0).unwrap().end() >= s.len(),
        };
        if let Some(opt_caps) = KEY_RE.captures(&key[..key.len() - 1]) {
            if let Some(short_key) = opt_caps.get(1) {
                var.key = short_key.as_str();
            }
            var.align = match opt_caps.get(2).map(|x| x.as_str()) {
                Some("<") => Alignment::Left,
                Some("^") => Alignment::Center,
                Some(">") => Alignment::Right,
                _ => Alignment::Left,
            };
            if let Some(width) = opt_caps.get(3) {
                var.width = Some(width.as_str().parse().unwrap());
            }
            if opt_caps.get(4).is_some() {
                var.truncate = true;
            }
            if let Some(style) = opt_caps.get(5) {
                var.style = Some(Style::from_dotted_str(style.as_str()));
            }
            if let Some(alt_style) = opt_caps.get(6) {
                var.alt_style = Some(Style::from_dotted_str(alt_style.as_str()));
            }
        }
        let mut rv = f(&var);
        if let Some(width) = var.width {
            rv = pad_str(&rv, width, var.align, var.truncate).to_string()
        }
        if let Some(s) = var.style {
            rv = s.apply_to(rv).to_string();
        }
        rv
    })
}

pub fn pad_str(s: &str, width: usize, align: Alignment, truncate: bool) -> Cow<'_, str> {
    let cols = measure_text_width(s);

    if cols >= width {
        return if truncate {
            Cow::Borrowed(s.get(..width).unwrap_or(s))
        } else {
            Cow::Borrowed(s)
        };
    }

    let diff = width.saturating_sub(cols);

    let (left_pad, right_pad) = match align {
        Alignment::Left => (0, diff),
        Alignment::Right => (diff, 0),
        Alignment::Center => (diff / 2, diff.saturating_sub(diff / 2)),
    };

    let mut rv = String::new();
    for _ in 0..left_pad {
        rv.push(' ');
    }
    rv.push_str(s);
    for _ in 0..right_pad {
        rv.push(' ');
    }
    Cow::Owned(rv)
}

#[test]
fn test_expand_template() {
    let rv = expand_template("{{ {foo} {bar} }}", |var| var.key.to_uppercase());
    assert_eq!(&rv, "{ FOO BAR }");
}

#[test]
fn test_expand_template_flags() {
    use console::set_colors_enabled;
    set_colors_enabled(true);

    let rv = expand_template("{foo:5}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        "XXX".into()
    });
    assert_eq!(&rv, "XXX  ");

    let rv = expand_template("{foo:.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, None);
        assert_eq!(var.align, Alignment::Left);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44mXXX\u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue/green.on_cyan}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        assert_eq!(var.alt_style, Some(Style::new().green().on_cyan()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");
}

#[test]
fn test_duration_stuff() {
    let duration = Duration::new(42, 100_000_000);
    let secs = duration_to_secs(duration);
    assert_eq!(secs_to_duration(secs), duration);
}
<commit_msg>Set initial 'started' value for Estimate::new() (#130)<commit_after>use std::borrow::Cow;
use std::time::{Duration, Instant};

use regex::{Captures, Regex};

use console::{measure_text_width, Style};

pub fn duration_to_secs(d: Duration) -> f64 {
    d.as_secs() as f64 + f64::from(d.subsec_nanos()) / 1_000_000_000f64
}

pub fn secs_to_duration(s: f64) -> Duration {
    let secs = s.trunc() as u64;
    let nanos = (s.fract() * 1_000_000_000f64) as u32;
    Duration::new(secs, nanos)
}

pub struct Estimate {
    buf: Vec<f64>,
    buf_cap: usize,
    last_idx: usize,
    started: Option<(Instant, u64)>,
}

impl Estimate {
    pub fn new() -> Estimate {
        Estimate {
            buf: vec![],
            buf_cap: 10,
            last_idx: 0,
            started: Some((<Instant>::now(), 0)),
        }
    }

    pub fn reset(&mut self) {
        self.buf.clear();
        self.last_idx = 0;
        self.started = None;
    }

    pub fn record_step(&mut self, value: u64) {
        // record initial position
        let (started_time, started_value) = match self.started {
            None => {
                let rv = (Instant::now(), value);
                self.started = Some(rv);
                rv
            }
            Some(value) => value,
        };

        let item = if value == 0 {
            0.0
        } else {
            let divisor = value.saturating_sub(started_value) as f64;
            if divisor == 0.0 {
                0.0
            } else {
                duration_to_secs(started_time.elapsed()) / divisor
            }
        };
        if self.buf.len() >= self.buf_cap {
            let idx = self.last_idx % self.buf.len();
            self.buf[idx] = item;
        } else {
            self.buf.push(item);
        }
        self.last_idx += 1;
    }

    pub fn time_per_step(&self) -> Duration {
        if self.buf.is_empty() {
            Duration::new(0, 0)
        } else {
            secs_to_duration(self.buf.iter().sum::<f64>() / self.buf.len() as f64)
        }
    }
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Alignment {
    Left,
    Center,
    Right,
}

#[derive(Debug)]
pub struct TemplateVar<'a> {
    pub key: &'a str,
    pub align: Alignment,
    pub truncate: bool,
    pub width: Option<usize>,
    pub style: Option<Style>,
    pub alt_style: Option<Style>,
    pub last_element: bool,
}

impl<'a> TemplateVar<'a> {
    pub fn duplicate_for_key<'b>(&self, key: &'b str) -> TemplateVar<'b> {
        TemplateVar {
            key,
            align: self.align,
            truncate: self.truncate,
            width: self.width,
            style: self.style.clone(),
            alt_style: self.alt_style.clone(),
            last_element: self.last_element,
        }
    }
}

pub fn expand_template<F: Fn(&TemplateVar<'_>) -> String>(s: &str, f: F) -> Cow<'_, str> {
    lazy_static::lazy_static! {
        static ref VAR_RE: Regex = Regex::new(r"(\}\})|\{(\{|[^}]+\})").unwrap();
        static ref KEY_RE: Regex = Regex::new(
            r"(?x)
                ([^:]+)
                (?:
                    :
                    ([<^>])?
                    ([0-9]+)?
                    (!)?
                    (?:\.([a-z_]+(?:\.[a-z_]+)*))?
                    (?:/([a-z_]+(?:\.[a-z_]+)*))?
                )?
            "
        )
        .unwrap();
    }
    VAR_RE.replace_all(s, |caps: &Captures<'_>| {
        if caps.get(1).is_some() {
            return "}".into();
        }
        let key = &caps[2];
        if key == "{" {
            return "{".into();
        }
        let mut var = TemplateVar {
            key,
            align: Alignment::Left,
            truncate: false,
            width: None,
            style: None,
            alt_style: None,
            last_element: caps.get(0).unwrap().end() >= s.len(),
        };
        if let Some(opt_caps) = KEY_RE.captures(&key[..key.len() - 1]) {
            if let Some(short_key) = opt_caps.get(1) {
                var.key = short_key.as_str();
            }
            var.align = match opt_caps.get(2).map(|x| x.as_str()) {
                Some("<") => Alignment::Left,
                Some("^") => Alignment::Center,
                Some(">") => Alignment::Right,
                _ => Alignment::Left,
            };
            if let Some(width) = opt_caps.get(3) {
                var.width = Some(width.as_str().parse().unwrap());
            }
            if opt_caps.get(4).is_some() {
                var.truncate = true;
            }
            if let Some(style) = opt_caps.get(5) {
                var.style = Some(Style::from_dotted_str(style.as_str()));
            }
            if let Some(alt_style) = opt_caps.get(6) {
                var.alt_style = Some(Style::from_dotted_str(alt_style.as_str()));
            }
        }
        let mut rv = f(&var);
        if let Some(width) = var.width {
            rv = pad_str(&rv, width, var.align, var.truncate).to_string()
        }
        if let Some(s) = var.style {
            rv = s.apply_to(rv).to_string();
        }
        rv
    })
}

pub fn pad_str(s: &str, width: usize, align: Alignment, truncate: bool) -> Cow<'_, str> {
    let cols = measure_text_width(s);

    if cols >= width {
        return if truncate {
            Cow::Borrowed(s.get(..width).unwrap_or(s))
        } else {
            Cow::Borrowed(s)
        };
    }

    let diff = width.saturating_sub(cols);

    let (left_pad, right_pad) = match align {
        Alignment::Left => (0, diff),
        Alignment::Right => (diff, 0),
        Alignment::Center => (diff / 2, diff.saturating_sub(diff / 2)),
    };

    let mut rv = String::new();
    for _ in 0..left_pad {
        rv.push(' ');
    }
    rv.push_str(s);
    for _ in 0..right_pad {
        rv.push(' ');
    }
    Cow::Owned(rv)
}

#[test]
fn test_expand_template() {
    let rv = expand_template("{{ {foo} {bar} }}", |var| var.key.to_uppercase());
    assert_eq!(&rv, "{ FOO BAR }");
}

#[test]
fn test_expand_template_flags() {
    use console::set_colors_enabled;
    set_colors_enabled(true);

    let rv = expand_template("{foo:5}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        "XXX".into()
    });
    assert_eq!(&rv, "XXX  ");

    let rv = expand_template("{foo:.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, None);
        assert_eq!(var.align, Alignment::Left);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44mXXX\u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue/green.on_cyan}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        assert_eq!(var.alt_style, Some(Style::new().green().on_cyan()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");
}

#[test]
fn test_duration_stuff() {
    let duration = Duration::new(42, 100_000_000);
    let secs = duration_to_secs(duration);
    assert_eq!(secs_to_duration(secs), duration);
}
<|endoftext|>


<commit_before>// Copyright 2015-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use ffi;
use translate::*;
use std::path::PathBuf;

/// Same as [`get_prgname()`].
///
/// [`get_prgname()`]: fn.get_prgname.html
pub fn get_program_name() -> Option<String> {
    get_prgname()
}

pub fn get_prgname() -> Option<String> {
    unsafe {
        from_glib_none(ffi::g_get_prgname())
    }
}

/// Same as [`set_prgname()`].
///
/// [`set_prgname()`]: fn.set_prgname.html
pub fn set_program_name(name: Option<&str>) {
    set_prgname(name)
}

pub fn set_prgname(name: Option<&str>) {
    unsafe {
        ffi::g_set_prgname(name.to_glib_none().0)
    }
}

#[cfg(not(windows))]
pub fn getenv(variable_name: &str) -> Option<String> {
    unsafe {
        from_glib_none(ffi::g_getenv(variable_name.to_glib_none().0))
    }
}

#[cfg(windows)]
pub fn getenv(variable_name: &str) -> Option<String> {
    use libc::c_char;
    extern "C" {
        fn g_getenv_utf8(variable: *const c_char) -> *const c_char;
    }

    unsafe {
        from_glib_none(g_getenv_utf8(variable_name.to_glib_none().0))
    }
}

#[cfg(not(windows))]
pub fn setenv(variable_name: &str, value: &str, overwrite: bool) -> bool {
    unsafe {
        from_glib(ffi::g_setenv(variable_name.to_glib_none().0,
                                value.to_glib_none().0,
                                overwrite.to_glib()))
    }
}

#[cfg(windows)]
pub fn setenv(variable_name: &str, value: &str, overwrite: bool) -> bool {
    use libc::c_char;
    extern "C" {
        fn g_setenv_utf8(variable: *const c_char, value: *const c_char, overwrite: ffi::gboolean) -> ffi::gboolean;
    }

    unsafe {
        from_glib(g_setenv_utf8(variable_name.to_glib_none().0,
                                value.to_glib_none().0,
                                overwrite.to_glib()))
    }
}

#[cfg(not(windows))]
pub fn unsetenv(variable_name: &str) {
    unsafe {
        ffi::g_unsetenv(variable_name.to_glib_none().0)
    }
}

#[cfg(windows)]
pub fn unsetenv(variable_name: &str) {
    use libc::c_char;
    extern "C" {
        fn g_unsetenv_utf8(variable: *const c_char);
    }

    unsafe {
        g_unsetenv_utf8(variable_name.to_glib_none().0)
    }
}

pub fn get_user_name() -> Option<String> {
    unsafe {
        from_glib_none(ffi::g_get_user_name())
    }
}

#[cfg(not(windows))]
pub fn get_current_dir() -> Option<PathBuf> {
    unsafe {
        from_glib_full(ffi::g_get_current_dir())
    }
}

#[cfg(windows)]
pub fn get_current_dir() -> Option<PathBuf> {
    use libc::c_char;
    extern "C" {
        fn g_get_current_dir_utf8() -> *mut c_char;
    }

    unsafe {
        from_glib_full(g_get_current_dir_utf8())
    }
}
<commit_msg>Use BoolError for g_setenv()<commit_after>// Copyright 2015-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use ffi;
use translate::*;
use std::path::PathBuf;
use error::BoolError;

/// Same as [`get_prgname()`].
///
/// [`get_prgname()`]: fn.get_prgname.html
pub fn get_program_name() -> Option<String> {
    get_prgname()
}

pub fn get_prgname() -> Option<String> {
    unsafe {
        from_glib_none(ffi::g_get_prgname())
    }
}

/// Same as [`set_prgname()`].
///
/// [`set_prgname()`]: fn.set_prgname.html
pub fn set_program_name(name: Option<&str>) {
    set_prgname(name)
}

pub fn set_prgname(name: Option<&str>) {
    unsafe {
        ffi::g_set_prgname(name.to_glib_none().0)
    }
}

#[cfg(not(windows))]
pub fn getenv(variable_name: &str) -> Option<String> {
    unsafe {
        from_glib_none(ffi::g_getenv(variable_name.to_glib_none().0))
    }
}

#[cfg(windows)]
pub fn getenv(variable_name: &str) -> Option<String> {
    use libc::c_char;
    extern "C" {
        fn g_getenv_utf8(variable: *const c_char) -> *const c_char;
    }

    unsafe {
        from_glib_none(g_getenv_utf8(variable_name.to_glib_none().0))
    }
}

#[cfg(not(windows))]
pub fn setenv(variable_name: &str, value: &str, overwrite: bool) -> Result<(), BoolError> {
    unsafe {
        BoolError::from_glib(ffi::g_setenv(variable_name.to_glib_none().0,
                                value.to_glib_none().0,
                                overwrite.to_glib()),
                             "Failed to set environment variable")
    }
}

#[cfg(windows)]
pub fn setenv(variable_name: &str, value: &str, overwrite: bool) -> Result<(), BoolError> {
    use libc::c_char;
    extern "C" {
        fn g_setenv_utf8(variable: *const c_char, value: *const c_char, overwrite: ffi::gboolean) -> ffi::gboolean;
    }

    unsafe {
        BoolError::from_glib(g_setenv_utf8(variable_name.to_glib_none().0,
                                value.to_glib_none().0,
                                overwrite.to_glib()),
                             "Failed to set environment variable")
    }
}

#[cfg(not(windows))]
pub fn unsetenv(variable_name: &str) {
    unsafe {
        ffi::g_unsetenv(variable_name.to_glib_none().0)
    }
}

#[cfg(windows)]
pub fn unsetenv(variable_name: &str) {
    use libc::c_char;
    extern "C" {
        fn g_unsetenv_utf8(variable: *const c_char);
    }

    unsafe {
        g_unsetenv_utf8(variable_name.to_glib_none().0)
    }
}

pub fn get_user_name() -> Option<String> {
    unsafe {
        from_glib_none(ffi::g_get_user_name())
    }
}

#[cfg(not(windows))]
pub fn get_current_dir() -> Option<PathBuf> {
    unsafe {
        from_glib_full(ffi::g_get_current_dir())
    }
}

#[cfg(windows)]
pub fn get_current_dir() -> Option<PathBuf> {
    use libc::c_char;
    extern "C" {
        fn g_get_current_dir_utf8() -> *mut c_char;
    }

    unsafe {
        from_glib_full(g_get_current_dir_utf8())
    }
}
<|endoftext|>


<commit_before>// Tests the optimised LMD Ghost Algorithm

extern crate beacon_chain;
extern crate bls;
extern crate db;
//extern crate env_logger; // for debugging
extern crate fork_choice;
extern crate hex;
extern crate log;
extern crate slot_clock;
extern crate types;
extern crate yaml_rust;

pub use beacon_chain::BeaconChain;
use bls::{PublicKey, Signature};
use db::stores::{BeaconBlockStore, BeaconStateStore};
use db::MemoryDB;
//use env_logger::{Builder, Env};
use fork_choice::{ForkChoice, ForkChoiceAlgorithm, LongestChain, OptimisedLMDGhost, SlowLMDGhost};
use ssz::ssz_encode;
use std::collections::HashMap;
use std::sync::Arc;
use std::{fs::File, io::prelude::*, path::PathBuf};
use types::{
    BeaconBlock, BeaconBlockBody, BeaconState, ChainSpec, Epoch, Eth1Data, Hash256, Slot, Validator,
};
use yaml_rust::yaml;

// Note: We Assume the block Id's are hex-encoded.

#[test]
fn test_optimised_lmd_ghost() {
    // set up logging
    //Builder::from_env(Env::default().default_filter_or("trace")).init();

    test_yaml_vectors(
        ForkChoiceAlgorithm::OptimisedLMDGhost,
        "tests/optimised_lmd_ghost_test_vectors.yaml",
        100,
    );
}

#[test]
fn test_slow_lmd_ghost() {
    test_yaml_vectors(
        ForkChoiceAlgorithm::SlowLMDGhost,
        "tests/lmd_ghost_test_vectors.yaml",
        100,
    );
}

#[test]
fn test_longest_chain() {
    test_yaml_vectors(
        ForkChoiceAlgorithm::LongestChain,
        "tests/longest_chain_test_vectors.yaml",
        100,
    );
}

// run a generic test over given YAML test vectors
fn test_yaml_vectors(
    fork_choice_algo: ForkChoiceAlgorithm,
    yaml_file_path: &str,
    emulated_validators: usize, // the number of validators used to give weights.
) {
    // load test cases from yaml
    let test_cases = load_test_cases_from_yaml(yaml_file_path);

    // default vars
    let spec = ChainSpec::foundation();
    let zero_hash = Hash256::zero();
    let eth1_data = Eth1Data {
        deposit_root: zero_hash.clone(),
        block_hash: zero_hash.clone(),
    };
    let randao_reveal = Signature::empty_signature();
    let signature = Signature::empty_signature();
    let body = BeaconBlockBody {
        proposer_slashings: vec![],
        attester_slashings: vec![],
        attestations: vec![],
        deposits: vec![],
        exits: vec![],
    };

    // process the tests
    for test_case in test_cases {
        // setup a fresh test
        let (mut fork_choice, block_store, state_root) =
            setup_inital_state(&fork_choice_algo, emulated_validators);

        // keep a hashmap of block_id's to block_hashes (random hashes to abstract block_id)
        //let mut block_id_map: HashMap<String, Hash256> = HashMap::new();
        // keep a list of hash to slot
        let mut block_slot: HashMap<Hash256, Slot> = HashMap::new();
        // assume the block tree is given to us in order.
        let mut genesis_hash = None;
        for block in test_case["blocks"].clone().into_vec().unwrap() {
            let block_id = block["id"].as_str().unwrap().to_string();
            let parent_id = block["parent"].as_str().unwrap().to_string();

            // default params for genesis
            let block_hash = id_to_hash(&block_id);
            let mut slot = spec.genesis_slot;
            let parent_root = id_to_hash(&parent_id);

            // set the slot and parent based off the YAML. Start with genesis;
            // if not the genesis, update slot
            if parent_id != block_id {
                // find parent slot
                slot = *(block_slot
                    .get(&parent_root)
                    .expect("Parent should have a slot number"))
                    + 1;
            } else {
                genesis_hash = Some(block_hash);
            }

            // update slot mapping
            block_slot.insert(block_hash, slot);

            // build the BeaconBlock
            let beacon_block = BeaconBlock {
                slot,
                parent_root,
                state_root: state_root.clone(),
                randao_reveal: randao_reveal.clone(),
                eth1_data: eth1_data.clone(),
                signature: signature.clone(),
                body: body.clone(),
            };

            // Store the block.
            block_store
                .put(&block_hash, &ssz_encode(&beacon_block)[..])
                .unwrap();

            // run add block for fork choice if not genesis
            if parent_id != block_id {
                fork_choice
                    .add_block(&beacon_block, &block_hash, &spec)
                    .unwrap();
            }
        }

        // add the weights (attestations)
        let mut current_validator = 0;
        for id_map in test_case["weights"].clone().into_vec().unwrap() {
            // get the block id and weights
            for (map_id, map_weight) in id_map.as_hash().unwrap().iter() {
                let id = map_id.as_str().unwrap();
                let block_root = id_to_hash(&id.to_string());
                let weight = map_weight.as_i64().unwrap();
                // we assume a validator has a value 1 and add an attestation for to achieve the
                // correct weight
                for _ in 0..weight {
                    assert!(
                        current_validator <= emulated_validators,
                        "Not enough validators to emulate weights"
                    );
                    fork_choice
                        .add_attestation(current_validator as u64, &block_root, &spec)
                        .unwrap();
                    current_validator += 1;
                }
            }
        }

        // everything is set up, run the fork choice, using genesis as the head
        let head = fork_choice
            .find_head(&genesis_hash.unwrap(), &spec)
            .unwrap();

        // compare the result to the expected test
        let success = test_case["heads"]
            .clone()
            .into_vec()
            .unwrap()
            .iter()
            .find(|heads| id_to_hash(&heads["id"].as_str().unwrap().to_string()) == head)
            .is_some();

        println!("Head found: {}", head);
        assert!(success, "Did not find one of the possible heads");
    }
}

// loads the test_cases from the supplied yaml file
fn load_test_cases_from_yaml(file_path: &str) -> Vec<yaml_rust::Yaml> {
    // load the yaml
    let mut file = {
        let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        file_path_buf.push(file_path);
        File::open(file_path_buf).unwrap()
    };
    let mut yaml_str = String::new();
    file.read_to_string(&mut yaml_str).unwrap();
    let docs = yaml::YamlLoader::load_from_str(&yaml_str).unwrap();
    let doc = &docs[0];
    doc["test_cases"].as_vec().unwrap().clone()
}

// initialise a single validator and state. All blocks will reference this state root.
fn setup_inital_state(
    fork_choice_algo: &ForkChoiceAlgorithm,
    no_validators: usize,
) -> (Box<ForkChoice>, Arc<BeaconBlockStore<MemoryDB>>, Hash256) {
    let zero_hash = Hash256::zero();

    let db = Arc::new(MemoryDB::open());
    let block_store = Arc::new(BeaconBlockStore::new(db.clone()));
    let state_store = Arc::new(BeaconStateStore::new(db.clone()));

    // the fork choice instantiation
    let fork_choice: Box<ForkChoice> = match fork_choice_algo {
        ForkChoiceAlgorithm::OptimisedLMDGhost => Box::new(OptimisedLMDGhost::new(
            block_store.clone(),
            state_store.clone(),
        )),
        ForkChoiceAlgorithm::SlowLMDGhost => {
            Box::new(SlowLMDGhost::new(block_store.clone(), state_store.clone()))
        }
        ForkChoiceAlgorithm::LongestChain => Box::new(LongestChain::new(block_store.clone())),
    };

    // misc vars for setting up the state
    let genesis_time = 1_550_381_159;

    let latest_eth1_data = Eth1Data {
        deposit_root: zero_hash.clone(),
        block_hash: zero_hash.clone(),
    };

    let initial_validator_deposits = vec![];
    let spec = ChainSpec::foundation();

    // create the state
    let mut state = BeaconState::genesis(
        genesis_time,
        initial_validator_deposits,
        latest_eth1_data,
        &spec,
    )
    .unwrap();

    let default_validator = Validator {
        pubkey: PublicKey::default(),
        withdrawal_credentials: zero_hash,
        activation_epoch: Epoch::from(0u64),
        exit_epoch: spec.far_future_epoch,
        withdrawal_epoch: spec.far_future_epoch,
        penalized_epoch: spec.far_future_epoch,
        status_flags: None,
    };
    // activate the validators
    for _ in 0..no_validators {
        state.validator_registry.push(default_validator.clone());
        state.validator_balances.push(32_000_000_000);
    }

    let state_root = state.canonical_root();
    state_store
        .put(&state_root, &ssz_encode(&state)[..])
        .unwrap();

    // return initialised vars
    (fork_choice, block_store, state_root)
}

// convert a block_id into a Hash256 -- assume input is hex encoded;
fn id_to_hash(id: &String) -> Hash256 {
    let bytes = hex::decode(id).expect("Block ID should be hex");

    let len = std::cmp::min(bytes.len(), 32);
    let mut fixed_bytes = [0u8; 32];
    for (index, byte) in bytes.iter().take(32).enumerate() {
        fixed_bytes[32 - len + index] = *byte;
    }
    Hash256::from(fixed_bytes)
}
<commit_msg>Correct minor comment.<commit_after>// Tests the available fork-choice algorithms

extern crate beacon_chain;
extern crate bls;
extern crate db;
//extern crate env_logger; // for debugging
extern crate fork_choice;
extern crate hex;
extern crate log;
extern crate slot_clock;
extern crate types;
extern crate yaml_rust;

pub use beacon_chain::BeaconChain;
use bls::{PublicKey, Signature};
use db::stores::{BeaconBlockStore, BeaconStateStore};
use db::MemoryDB;
//use env_logger::{Builder, Env};
use fork_choice::{ForkChoice, ForkChoiceAlgorithm, LongestChain, OptimisedLMDGhost, SlowLMDGhost};
use ssz::ssz_encode;
use std::collections::HashMap;
use std::sync::Arc;
use std::{fs::File, io::prelude::*, path::PathBuf};
use types::{
    BeaconBlock, BeaconBlockBody, BeaconState, ChainSpec, Epoch, Eth1Data, Hash256, Slot, Validator,
};
use yaml_rust::yaml;

// Note: We Assume the block Id's are hex-encoded.

#[test]
fn test_optimised_lmd_ghost() {
    // set up logging
    //Builder::from_env(Env::default().default_filter_or("trace")).init();

    test_yaml_vectors(
        ForkChoiceAlgorithm::OptimisedLMDGhost,
        "tests/optimised_lmd_ghost_test_vectors.yaml",
        100,
    );
}

#[test]
fn test_slow_lmd_ghost() {
    test_yaml_vectors(
        ForkChoiceAlgorithm::SlowLMDGhost,
        "tests/lmd_ghost_test_vectors.yaml",
        100,
    );
}

#[test]
fn test_longest_chain() {
    test_yaml_vectors(
        ForkChoiceAlgorithm::LongestChain,
        "tests/longest_chain_test_vectors.yaml",
        100,
    );
}

// run a generic test over given YAML test vectors
fn test_yaml_vectors(
    fork_choice_algo: ForkChoiceAlgorithm,
    yaml_file_path: &str,
    emulated_validators: usize, // the number of validators used to give weights.
) {
    // load test cases from yaml
    let test_cases = load_test_cases_from_yaml(yaml_file_path);

    // default vars
    let spec = ChainSpec::foundation();
    let zero_hash = Hash256::zero();
    let eth1_data = Eth1Data {
        deposit_root: zero_hash.clone(),
        block_hash: zero_hash.clone(),
    };
    let randao_reveal = Signature::empty_signature();
    let signature = Signature::empty_signature();
    let body = BeaconBlockBody {
        proposer_slashings: vec![],
        attester_slashings: vec![],
        attestations: vec![],
        deposits: vec![],
        exits: vec![],
    };

    // process the tests
    for test_case in test_cases {
        // setup a fresh test
        let (mut fork_choice, block_store, state_root) =
            setup_inital_state(&fork_choice_algo, emulated_validators);

        // keep a hashmap of block_id's to block_hashes (random hashes to abstract block_id)
        //let mut block_id_map: HashMap<String, Hash256> = HashMap::new();
        // keep a list of hash to slot
        let mut block_slot: HashMap<Hash256, Slot> = HashMap::new();
        // assume the block tree is given to us in order.
        let mut genesis_hash = None;
        for block in test_case["blocks"].clone().into_vec().unwrap() {
            let block_id = block["id"].as_str().unwrap().to_string();
            let parent_id = block["parent"].as_str().unwrap().to_string();

            // default params for genesis
            let block_hash = id_to_hash(&block_id);
            let mut slot = spec.genesis_slot;
            let parent_root = id_to_hash(&parent_id);

            // set the slot and parent based off the YAML. Start with genesis;
            // if not the genesis, update slot
            if parent_id != block_id {
                // find parent slot
                slot = *(block_slot
                    .get(&parent_root)
                    .expect("Parent should have a slot number"))
                    + 1;
            } else {
                genesis_hash = Some(block_hash);
            }

            // update slot mapping
            block_slot.insert(block_hash, slot);

            // build the BeaconBlock
            let beacon_block = BeaconBlock {
                slot,
                parent_root,
                state_root: state_root.clone(),
                randao_reveal: randao_reveal.clone(),
                eth1_data: eth1_data.clone(),
                signature: signature.clone(),
                body: body.clone(),
            };

            // Store the block.
            block_store
                .put(&block_hash, &ssz_encode(&beacon_block)[..])
                .unwrap();

            // run add block for fork choice if not genesis
            if parent_id != block_id {
                fork_choice
                    .add_block(&beacon_block, &block_hash, &spec)
                    .unwrap();
            }
        }

        // add the weights (attestations)
        let mut current_validator = 0;
        for id_map in test_case["weights"].clone().into_vec().unwrap() {
            // get the block id and weights
            for (map_id, map_weight) in id_map.as_hash().unwrap().iter() {
                let id = map_id.as_str().unwrap();
                let block_root = id_to_hash(&id.to_string());
                let weight = map_weight.as_i64().unwrap();
                // we assume a validator has a value 1 and add an attestation for to achieve the
                // correct weight
                for _ in 0..weight {
                    assert!(
                        current_validator <= emulated_validators,
                        "Not enough validators to emulate weights"
                    );
                    fork_choice
                        .add_attestation(current_validator as u64, &block_root, &spec)
                        .unwrap();
                    current_validator += 1;
                }
            }
        }

        // everything is set up, run the fork choice, using genesis as the head
        let head = fork_choice
            .find_head(&genesis_hash.unwrap(), &spec)
            .unwrap();

        // compare the result to the expected test
        let success = test_case["heads"]
            .clone()
            .into_vec()
            .unwrap()
            .iter()
            .find(|heads| id_to_hash(&heads["id"].as_str().unwrap().to_string()) == head)
            .is_some();

        println!("Head found: {}", head);
        assert!(success, "Did not find one of the possible heads");
    }
}

// loads the test_cases from the supplied yaml file
fn load_test_cases_from_yaml(file_path: &str) -> Vec<yaml_rust::Yaml> {
    // load the yaml
    let mut file = {
        let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        file_path_buf.push(file_path);
        File::open(file_path_buf).unwrap()
    };
    let mut yaml_str = String::new();
    file.read_to_string(&mut yaml_str).unwrap();
    let docs = yaml::YamlLoader::load_from_str(&yaml_str).unwrap();
    let doc = &docs[0];
    doc["test_cases"].as_vec().unwrap().clone()
}

// initialise a single validator and state. All blocks will reference this state root.
fn setup_inital_state(
    fork_choice_algo: &ForkChoiceAlgorithm,
    no_validators: usize,
) -> (Box<ForkChoice>, Arc<BeaconBlockStore<MemoryDB>>, Hash256) {
    let zero_hash = Hash256::zero();

    let db = Arc::new(MemoryDB::open());
    let block_store = Arc::new(BeaconBlockStore::new(db.clone()));
    let state_store = Arc::new(BeaconStateStore::new(db.clone()));

    // the fork choice instantiation
    let fork_choice: Box<ForkChoice> = match fork_choice_algo {
        ForkChoiceAlgorithm::OptimisedLMDGhost => Box::new(OptimisedLMDGhost::new(
            block_store.clone(),
            state_store.clone(),
        )),
        ForkChoiceAlgorithm::SlowLMDGhost => {
            Box::new(SlowLMDGhost::new(block_store.clone(), state_store.clone()))
        }
        ForkChoiceAlgorithm::LongestChain => Box::new(LongestChain::new(block_store.clone())),
    };

    // misc vars for setting up the state
    let genesis_time = 1_550_381_159;

    let latest_eth1_data = Eth1Data {
        deposit_root: zero_hash.clone(),
        block_hash: zero_hash.clone(),
    };

    let initial_validator_deposits = vec![];
    let spec = ChainSpec::foundation();

    // create the state
    let mut state = BeaconState::genesis(
        genesis_time,
        initial_validator_deposits,
        latest_eth1_data,
        &spec,
    )
    .unwrap();

    let default_validator = Validator {
        pubkey: PublicKey::default(),
        withdrawal_credentials: zero_hash,
        activation_epoch: Epoch::from(0u64),
        exit_epoch: spec.far_future_epoch,
        withdrawal_epoch: spec.far_future_epoch,
        penalized_epoch: spec.far_future_epoch,
        status_flags: None,
    };
    // activate the validators
    for _ in 0..no_validators {
        state.validator_registry.push(default_validator.clone());
        state.validator_balances.push(32_000_000_000);
    }

    let state_root = state.canonical_root();
    state_store
        .put(&state_root, &ssz_encode(&state)[..])
        .unwrap();

    // return initialised vars
    (fork_choice, block_store, state_root)
}

// convert a block_id into a Hash256 -- assume input is hex encoded;
fn id_to_hash(id: &String) -> Hash256 {
    let bytes = hex::decode(id).expect("Block ID should be hex");

    let len = std::cmp::min(bytes.len(), 32);
    let mut fixed_bytes = [0u8; 32];
    for (index, byte) in bytes.iter().take(32).enumerate() {
        fixed_bytes[32 - len + index] = *byte;
    }
    Hash256::from(fixed_bytes)
}
<|endoftext|>