<commit_before>use std::io::{self, Write};
use tinf::Desc;
use tvis_util::{size, Handle, TerminalMode};
use {CLIStream, Color, DoStyle, LockableStream, Result, Stream, WinSize};

/// A styled stream using terminfo escape sequences.
///
/// Instances are lightweight, using only four escape sequences
/// (instead of copying entire terminfo descriptions).
pub struct TermStream<T> {
    cap_reset: Vec<u8>,
    cap_fg: Vec<u8>,
    cap_em: Vec<u8>,
    cap_rewind: Vec<u8>,
    w: T,
    defsz: WinSize,
    mode: TerminalMode,
}

impl TermStream<io::Stdout> {
    /// A `TermStream` that wraps `std::io::stdout()`, using the
    /// current terminal description.
    pub fn stdout(do_style: DoStyle) -> TermStream<io::Stdout> {
        let mode = Handle::Stdout.terminal_mode();
        TermStream::std(mode, io::stdout(), do_style)
    }
}

impl TermStream<io::Stderr> {
    /// A `TermStream` that wraps `std::io::stderr()`, using the
    /// current terminal description.
    pub fn stderr(do_style: DoStyle) -> TermStream<io::Stderr> {
        let mode = Handle::Stderr.terminal_mode();
        TermStream::std(mode, io::stderr(), do_style)
    }
}

// Thanks to /u/cbreeden for help with lifetimes.
impl LockableStream for TermStream<io::Stdout> {
    fn lock<'a>(&'a self) -> Box<CLIStream + 'a> {
        let locked = TermStream {
            cap_reset: self.cap_reset.clone(),
            cap_fg: self.cap_fg.clone(),
            cap_em: self.cap_em.clone(),
            cap_rewind: self.cap_rewind.clone(),
            w: self.w.lock(),
            defsz: self.defsz,
            mode: self.mode,
        };
        Box::new(locked)
    }
}

impl LockableStream for TermStream<io::Stderr> {
    fn lock<'a>(&'a self) -> Box<CLIStream + 'a> {
        let locked = TermStream {
            cap_reset: self.cap_reset.clone(),
            cap_fg: self.cap_fg.clone(),
            cap_em: self.cap_em.clone(),
            cap_rewind: self.cap_rewind.clone(),
            w: self.w.lock(),
            defsz: self.defsz,
            mode: self.mode,
        };
        Box::new(locked)
    }
}

impl<T: io::Write> TermStream<T> {
    fn new(
        w: T,
        desc: &Desc,
        do_style: bool,
        mode: TerminalMode,
    ) -> TermStream<T> {
        use self::TerminalMode::*;
        use tinf::cap;

        let mut term_stream = TermStream {
            cap_reset: Vec::new(),
            cap_fg: Vec::new(),
            cap_em: Vec::new(),
            cap_rewind: if mode == Redir {
                Vec::new()
            } else {
                desc[cap::cuu].to_vec()
            },
            w,
            defsz: get_default_size(mode, desc),
            mode: mode,
        };
        let do_style = do_style && !desc[cap::sgr0].is_empty();
        if do_style {
            term_stream.cap_reset = desc[cap::sgr0].to_vec();
            term_stream.cap_fg = desc[cap::setaf].to_vec();
            term_stream.cap_em = get_em(desc);
        }
        term_stream
    }

    pub(super) fn std(
        mode: TerminalMode,
        w: T,
        do_style: DoStyle,
    ) -> TermStream<T> {
        use self::TerminalMode::*;
        use self::DoStyle::*;

        let use_style = match mode {
            Redir => do_style == Always,
            _ => do_style != Never,
        };

        match mode {
            #[cfg(windows)]
            Win10 => TermStream {
                cap_reset: b"\x1b[0m".to_vec(),
                cap_fg: b"\x1b[3%p1%dm".to_vec(),
                cap_em: b"\x1b[1m".to_vec(),
                cap_rewind: b"\x1b[%p1%dA".to_vec(),
                w,
                defsz: size::get_default_console_size(),
                mode: TerminalMode::Win10,
            },
            _ => TermStream::new(w, Desc::current(), use_style, mode),
        }
    }

    fn rewind_lines(&mut self, count: u16) -> Result<()> {
        if self.mode == TerminalMode::Redir {
            panic!("cli method called on redirected stream");
        }
        self.flush()?;
        if count > 0 {
            self.write_all(&[b'\r'])?;
        }
        if count > 1 {
            ::tinf::tparm(
                &mut self.w,
                &self.cap_rewind,
                &mut params!(count - 1),
                &mut ::tinf::Vars::new(),
            )?;
        }
        Ok(())
    }

    fn get_size(&self, handle: Handle) -> WinSize {
        if self.mode == TerminalMode::Redir {
            panic!("cli method called on redirected stream");
        }
        match size::get_size(handle) {
            Some(sz) => sz,
            None => self.defsz,
        }
    }
}

impl<T: io::Write> io::Write for TermStream<T> {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        self.w.write(data)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.w.flush()
    }
}

/// The `Stream` methods of this implementation may return
/// an`Err(`[`Error`](struct.Error.html)`)` if there is an I/O error
/// writing to the wrapped stream, or if the `TermStream` object was
/// created from a broken terminfo description with invalid escape
/// sequences.
impl<T: io::Write> Stream for TermStream<T> {
    // Note that vars is not persistent between calls to tparm; for
    // the escape sequences we use, this is fine unless you are using
    // certain ancient terminals made by Data General or Wyse.

    /// Sends the `sgr0` escape sequence.
    fn reset(&mut self) -> Result<()> {
        ::tinf::tparm(
            &mut self.w,
            &self.cap_reset,
            &mut params!(),
            &mut ::tinf::Vars::new(),
        )?;
        Ok(())
    }

    /// Sends the `setaf` escape sequence.
    fn fg(&mut self, fg: Color) -> Result<()> {
        ::tinf::tparm(
            &mut self.w,
            &self.cap_fg,
            &mut params!(fg.0),
            &mut ::tinf::Vars::new(),
        )?;
        Ok(())
    }

    /// Sends the `bold`, `smul`, or `smso` escape sequence (depending
    /// on which is supported by the terminfo description).
    fn em(&mut self) -> Result<()> {
        ::tinf::tparm(
            &mut self.w,
            &self.cap_em,
            &mut params!(),
            &mut ::tinf::Vars::new(),
        )?;
        Ok(())
    }

    fn is_cli(&self) -> bool {
        self.mode != TerminalMode::Redir
    }
}

fn get_default_size(mode: TerminalMode, desc: &Desc) -> WinSize {
    #[cfg(windows)]
    use self::TerminalMode::*;
    use tinf::cap;

    match mode {
        #[cfg(windows)]
        Win10 | Console => size::get_default_console_size(),
        _ => {
            let cols = match desc[cap::cols] {
                0 | 0xffff => 80,
                v => i32::from(v),
            };
            let rows = match desc[cap::lines] {
                0 | 0xffff => 24,
                v => i32::from(v),
            };
            WinSize { cols, rows }
        }
    }
}

fn get_em(desc: &Desc) -> Vec<u8> {
    use tinf::cap;

    let ncv = match desc[cap::ncv] {
        0xffff => 0,
        n => n,
    };
    let color = desc[cap::colors] != 0xffff && desc[cap::colors] != 0;
    if !desc[cap::bold].is_empty() && !(color && (ncv & 0x20 != 0)) {
        return desc[cap::bold].to_vec();
    }
    if !desc[cap::smul].is_empty() && !(color && (ncv & 0x02 != 0)) {
        return desc[cap::smul].to_vec();
    }
    if !desc[cap::smso].is_empty() && !(color && (ncv & 0x01 != 0)) {
        return desc[cap::smso].to_vec();
    }

    Vec::new()
}

impl CLIStream for TermStream<io::Stdout> {
    fn rewind_lines(&mut self, count: u16) -> Result<()> {
        self.rewind_lines(count)
    }

    fn get_size(&self) -> WinSize {
        // if cygwin, call with locked self.w else below
        self.get_size(Handle::Stdout)
    }
}

impl CLIStream for TermStream<io::Stderr> {
    fn rewind_lines(&mut self, count: u16) -> Result<()> {
        self.rewind_lines(count)
    }

    fn get_size(&self) -> WinSize {
        // if cygwin, call with locked self.w?
        self.get_size(Handle::Stderr)
    }
}

impl<'a> CLIStream for TermStream<io::StdoutLock<'a>> {
    fn rewind_lines(&mut self, count: u16) -> Result<()> {
        self.rewind_lines(count)
    }

    fn get_size(&self) -> WinSize {
        // if cygwin, call with self.w?
        self.get_size(Handle::Stdout)
    }
}

impl<'a> CLIStream for TermStream<io::StderrLock<'a>> {
    fn rewind_lines(&mut self, count: u16) -> Result<()> {
        self.rewind_lines(count)
    }

    fn get_size(&self) -> WinSize {
        // if cygwin, call with self.w?
        self.get_size(Handle::Stderr)
    }
}
<commit_msg>stijl: work with latest tvis_util<commit_after>use std::io::{self, Write};
use tinf::Desc;
use tvis_util::{size, Handle, TerminalMode};
use {CLIStream, Color, DoStyle, LockableStream, Result, Stream, WinSize};

/// A styled stream using terminfo escape sequences.
///
/// Instances are lightweight, using only four escape sequences
/// (instead of copying entire terminfo descriptions).
pub struct TermStream<T> {
    cap_reset: Vec<u8>,
    cap_fg: Vec<u8>,
    cap_em: Vec<u8>,
    cap_rewind: Vec<u8>,
    w: T,
    defsz: WinSize,
    mode: TerminalMode,
}

impl TermStream<io::Stdout> {
    /// A `TermStream` that wraps `std::io::stdout()`, using the
    /// current terminal description.
    pub fn stdout(do_style: DoStyle) -> TermStream<io::Stdout> {
        let mode = Handle::Stdout.terminal_mode();
        TermStream::std(mode, io::stdout(), do_style)
    }
}

impl TermStream<io::Stderr> {
    /// A `TermStream` that wraps `std::io::stderr()`, using the
    /// current terminal description.
    pub fn stderr(do_style: DoStyle) -> TermStream<io::Stderr> {
        let mode = Handle::Stderr.terminal_mode();
        TermStream::std(mode, io::stderr(), do_style)
    }
}

// Thanks to /u/cbreeden for help with lifetimes.
impl LockableStream for TermStream<io::Stdout> {
    fn lock<'a>(&'a self) -> Box<CLIStream + 'a> {
        let locked = TermStream {
            cap_reset: self.cap_reset.clone(),
            cap_fg: self.cap_fg.clone(),
            cap_em: self.cap_em.clone(),
            cap_rewind: self.cap_rewind.clone(),
            w: self.w.lock(),
            defsz: self.defsz,
            mode: self.mode,
        };
        Box::new(locked)
    }
}

impl LockableStream for TermStream<io::Stderr> {
    fn lock<'a>(&'a self) -> Box<CLIStream + 'a> {
        let locked = TermStream {
            cap_reset: self.cap_reset.clone(),
            cap_fg: self.cap_fg.clone(),
            cap_em: self.cap_em.clone(),
            cap_rewind: self.cap_rewind.clone(),
            w: self.w.lock(),
            defsz: self.defsz,
            mode: self.mode,
        };
        Box::new(locked)
    }
}

impl<T: io::Write> TermStream<T> {
    fn new(
        w: T,
        desc: &Desc,
        do_style: bool,
        mode: TerminalMode,
    ) -> TermStream<T> {
        use self::TerminalMode::*;
        use tinf::cap;

        let mut term_stream = TermStream {
            cap_reset: Vec::new(),
            cap_fg: Vec::new(),
            cap_em: Vec::new(),
            cap_rewind: if mode == Redir {
                Vec::new()
            } else {
                desc[cap::cuu].to_vec()
            },
            w,
            defsz: get_default_size(mode, desc),
            mode: mode,
        };
        let do_style = do_style && !desc[cap::sgr0].is_empty();
        if do_style {
            term_stream.cap_reset = desc[cap::sgr0].to_vec();
            term_stream.cap_fg = desc[cap::setaf].to_vec();
            term_stream.cap_em = get_em(desc);
        }
        term_stream
    }

    pub(super) fn std(
        mode: TerminalMode,
        w: T,
        do_style: DoStyle,
    ) -> TermStream<T> {
        use self::TerminalMode::*;
        use self::DoStyle::*;

        let use_style = match mode {
            Redir => do_style == Always,
            _ => do_style != Never,
        };

        match mode {
            #[cfg(windows)]
            Win10 => TermStream {
                cap_reset: b"\x1b[0m".to_vec(),
                cap_fg: b"\x1b[3%p1%dm".to_vec(),
                cap_em: b"\x1b[1m".to_vec(),
                cap_rewind: b"\x1b[%p1%dA".to_vec(),
                w,
                defsz: size::get_default_console_size(),
                mode: TerminalMode::Win10,
            },
            _ => TermStream::new(w, Desc::current(), use_style, mode),
        }
    }

    fn rewind_lines(&mut self, count: u16) -> Result<()> {
        if self.mode == TerminalMode::Redir {
            panic!("cli method called on redirected stream");
        }
        self.flush()?;
        if count > 0 {
            self.write_all(&[b'\r'])?;
        }
        if count > 1 {
            ::tinf::tparm(
                &mut self.w,
                &self.cap_rewind,
                &mut params!(count - 1),
                &mut ::tinf::Vars::new(),
            )?;
        }
        Ok(())
    }

    fn get_size(&self, handle: Handle) -> WinSize {
        if self.mode == TerminalMode::Redir {
            panic!("cli method called on redirected stream");
        }
        match size::get_size(handle) {
            Some(sz) => sz,
            None => self.defsz,
        }
    }
}

impl<T: io::Write> io::Write for TermStream<T> {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        self.w.write(data)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.w.flush()
    }
}

/// The `Stream` methods of this implementation may return
/// an`Err(`[`Error`](struct.Error.html)`)` if there is an I/O error
/// writing to the wrapped stream, or if the `TermStream` object was
/// created from a broken terminfo description with invalid escape
/// sequences.
impl<T: io::Write> Stream for TermStream<T> {
    // Note that vars is not persistent between calls to tparm; for
    // the escape sequences we use, this is fine unless you are using
    // certain ancient terminals made by Data General or Wyse.

    /// Sends the `sgr0` escape sequence.
    fn reset(&mut self) -> Result<()> {
        ::tinf::tparm(
            &mut self.w,
            &self.cap_reset,
            &mut params!(),
            &mut ::tinf::Vars::new(),
        )?;
        Ok(())
    }

    /// Sends the `setaf` escape sequence.
    fn fg(&mut self, fg: Color) -> Result<()> {
        ::tinf::tparm(
            &mut self.w,
            &self.cap_fg,
            &mut params!(fg.0),
            &mut ::tinf::Vars::new(),
        )?;
        Ok(())
    }

    /// Sends the `bold`, `smul`, or `smso` escape sequence (depending
    /// on which is supported by the terminfo description).
    fn em(&mut self) -> Result<()> {
        ::tinf::tparm(
            &mut self.w,
            &self.cap_em,
            &mut params!(),
            &mut ::tinf::Vars::new(),
        )?;
        Ok(())
    }

    fn is_cli(&self) -> bool {
        self.mode != TerminalMode::Redir
    }
}

fn get_default_size(mode: TerminalMode, desc: &Desc) -> WinSize {
    #[cfg(windows)]
    use self::TerminalMode::*;
    use tinf::cap;

    match mode {
        #[cfg(windows)]
        Win10 | Console => size::get_default_console_size(),
        _ => {
            let cols = match desc[cap::cols] {
                0 | 0xffff => 80,
                v => u32::from(v),
            };
            let rows = match desc[cap::lines] {
                0 | 0xffff => 24,
                v => u32::from(v),
            };
            WinSize { cols, rows }
        }
    }
}

fn get_em(desc: &Desc) -> Vec<u8> {
    use tinf::cap;

    let ncv = match desc[cap::ncv] {
        0xffff => 0,
        n => n,
    };
    let color = desc[cap::colors] != 0xffff && desc[cap::colors] != 0;
    if !desc[cap::bold].is_empty() && !(color && (ncv & 0x20 != 0)) {
        return desc[cap::bold].to_vec();
    }
    if !desc[cap::smul].is_empty() && !(color && (ncv & 0x02 != 0)) {
        return desc[cap::smul].to_vec();
    }
    if !desc[cap::smso].is_empty() && !(color && (ncv & 0x01 != 0)) {
        return desc[cap::smso].to_vec();
    }

    Vec::new()
}

impl CLIStream for TermStream<io::Stdout> {
    fn rewind_lines(&mut self, count: u16) -> Result<()> {
        self.rewind_lines(count)
    }

    fn get_size(&self) -> WinSize {
        // if cygwin, call with locked self.w else below
        self.get_size(Handle::Stdout)
    }
}

impl CLIStream for TermStream<io::Stderr> {
    fn rewind_lines(&mut self, count: u16) -> Result<()> {
        self.rewind_lines(count)
    }

    fn get_size(&self) -> WinSize {
        // if cygwin, call with locked self.w?
        self.get_size(Handle::Stderr)
    }
}

impl<'a> CLIStream for TermStream<io::StdoutLock<'a>> {
    fn rewind_lines(&mut self, count: u16) -> Result<()> {
        self.rewind_lines(count)
    }

    fn get_size(&self) -> WinSize {
        // if cygwin, call with self.w?
        self.get_size(Handle::Stdout)
    }
}

impl<'a> CLIStream for TermStream<io::StderrLock<'a>> {
    fn rewind_lines(&mut self, count: u16) -> Result<()> {
        self.rewind_lines(count)
    }

    fn get_size(&self) -> WinSize {
        // if cygwin, call with self.w?
        self.get_size(Handle::Stderr)
    }
}
<|endoftext|>


<commit_before>use color::Colorf32;
use fastup::{self, Node, parse_for_first_node, parse_color};
use stats_once::{MEM_TOTAL, MEM_USED};
use super::error_node;
use std::ops::{Add, Sub, Mul, Div};

#[derive(Debug, Copy, Clone, Default)]
pub struct Widget;

impl super::Widget for Widget {
    fn expand(&self, args: Vec<String>) -> Node {
        match args.len() {
            2 => node_from_args(args),
            _ => error_node("(ccdd44: mem) takes 2 arguments: color-cold\\|color-hot"),
        }
    }
}

fn node_from_args(args: Vec<String>) -> Node {
    try_node_from_args(args).unwrap_or_else(|e| error_node(&e))
}

fn try_node_from_args(args: Vec<String>) -> Result<Node, String> {
    assert_eq!(args.len(), 2);

    let cold = parse_color(&args[0]).map_err(escape_fastup)?;
    let hot = parse_color(&args[1]).map_err(escape_fastup)?;

    Ok(mem_node(cold.into(), hot.into()))
}

fn mem_node(cold: Colorf32, hot: Colorf32) -> Node {
    const NONE_LIGHTNESS: f32 = 0.15;
    const FULL_LIGHTNESS: f32 = 0.7;
    const TEXT_LIGHTNESS_BOOST_NONE: f32 = 0.15;
    const TEXT_LIGHTNESS_BOOST_FULL: f32 = 0.25;

    let mem_used = *MEM_USED;
    let mem_total = *MEM_TOTAL;
    let mem_usage = (mem_used as f32) / (mem_total as f32);

    let none_cold = cold.set_lightness(NONE_LIGHTNESS);
    let none_hot = hot.set_lightness(NONE_LIGHTNESS);
    let none = none_cold.mix(none_hot, mem_usage);

    let full_lightness = remap(mem_usage, 0.0, 1.0, NONE_LIGHTNESS, FULL_LIGHTNESS);
    let full_cold = cold.set_lightness(full_lightness);
    let full_hot = hot.set_lightness(full_lightness);
    let full = full_cold.mix(full_hot, mem_usage);

    let text_lightness_boost = remap(mem_usage, 0.0, 1.0, TEXT_LIGHTNESS_BOOST_NONE, TEXT_LIGHTNESS_BOOST_FULL);
    let text_lightness = full_lightness + text_lightness_boost;
    let text_cold = cold.set_lightness(text_lightness);
    let text_hot = hot.set_lightness(text_lightness);
    let text = text_cold.mix(text_hot, mem_usage);

    let percent = (100.0 * mem_usage) as i32;
    let usage = format!(" {:>4}M{:>3}% ", mem_used, percent);
    let width = usage.len() as i32;

    let (fully_filled_cells, partial_fill_rate) = quantize(mem_usage, width);
    let partial = none.mix(full, partial_fill_rate);

    let none = none.clamp_to_888();
    let full = full.clamp_to_888();
    let text = text.clamp_to_888();
    let partial = partial.clamp_to_888();

    let (full_cells, partial_cell, none_cells) = zipper_split(&usage, fully_filled_cells as usize);
    let full_cells = format!("[{}:{}]", full, escape_fastup(full_cells));
    let partial_cell = format!("[{}:{}]", partial, escape_fastup(partial_cell));
    let none_cells = format!("[{}:{}]", none, escape_fastup(none_cells));
    let usage = format!("{}{}{}", full_cells, partial_cell, none_cells);

    let node = format!("({}:{})", text, usage);
    parse_for_first_node(&node).unwrap()
}

fn escape_fastup<T>(input: T) -> String
    where T: ToString
{
    fastup::escape_for_text(&input.to_string())
}

/// Linearly remap `x` from inclusive range [`xa`, `xb`] to
/// inclusive range [`ra`, `rb`]. `x` must fall inside [`xa`, `xb`],
/// or panic will be issued.
fn remap<T>(x: T, xa: T, xb: T, ra: T, rb: T) -> T
    where T: PartialOrd + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + Copy
{
    assert!(xa <= x && x <= xb);
    (x - xa) * (rb - ra) / (xb - xa) + ra
}

/// Returns (quantized, remaining)
fn quantize(x: f32, steps: i32) -> (i32, f32) {
    let x = x * (steps as f32);
    let quantized = x.floor();
    let remaining = x - quantized;
    let quantized = quantized as i32;
    (quantized, remaining)
}

fn byte_index_from_char_index(s: &str, nchar: usize) -> usize {
    s.chars().take(nchar).map(|ch| ch.len_utf8()).sum()
}

fn split_at_char_index(s: &str, at: usize) -> (&str, &str) {
    let at = byte_index_from_char_index(s, at);
    s.split_at(at)
}

/// ```text
/// zipper_split("hello world", 3)
///                  ^--------- at = 3
///
/// Returns ("hel", "l", "o world")
/// ```
fn zipper_split(s: &str, at: usize) -> (&str, &str, &str) {
    let (first, rest) = split_at_char_index(s, at);
    let (elem, last) = split_at_char_index(rest, 1);
    (first, elem, last)
}

<commit_msg>widget: + debug options for <mem><commit_after>use color::Colorf32;
use fastup::{self, Node, parse_for_first_node, parse_color};
use stats_once::{MEM_TOTAL, MEM_USED};
use super::error_node;
use std::ops::{Add, Sub, Mul, Div};

#[derive(Debug, Copy, Clone, Default)]
pub struct Widget;

impl super::Widget for Widget {
    fn expand(&self, args: Vec<String>) -> Node {
        match args.len() {
            2 => node_from_args(args),
            3 => node_from_args(args),  // debug only
            _ => error_node("(ccdd44: mem) takes 2 arguments: color-cold\\|color-hot"),
        }
    }
}

fn node_from_args(args: Vec<String>) -> Node {
    try_node_from_args(args).unwrap_or_else(|e| error_node(&e))
}

fn try_node_from_args(args: Vec<String>) -> Result<Node, String> {
    assert!(args.len() == 2 || args.len() == 3);

    let cold = parse_color(&args[0]).map_err(escape_fastup)?;
    let hot = parse_color(&args[1]).map_err(escape_fastup)?;
    let mem_total = *MEM_TOTAL;
    let mem_used = {
        if args.len() == 3 {
            args[2].parse().map_err(escape_fastup)?
        } else {
            *MEM_USED
        }
    };

    Ok(mem_node(cold.into(), hot.into(), mem_used, mem_total))
}

fn mem_node(cold: Colorf32, hot: Colorf32, mem_used: i32, mem_total: i32) -> Node {
    const NONE_LIGHTNESS: f32 = 0.15;
    const FULL_LIGHTNESS: f32 = 0.7;
    const TEXT_LIGHTNESS_BOOST_NONE: f32 = 0.15;
    const TEXT_LIGHTNESS_BOOST_FULL: f32 = 0.25;

    let mem_usage = (mem_used as f32) / (mem_total as f32);

    let none_cold = cold.set_lightness(NONE_LIGHTNESS);
    let none_hot = hot.set_lightness(NONE_LIGHTNESS);
    let none = none_cold.mix(none_hot, mem_usage);

    let full_lightness = remap(mem_usage, 0.0, 1.0, NONE_LIGHTNESS, FULL_LIGHTNESS);
    let full_cold = cold.set_lightness(full_lightness);
    let full_hot = hot.set_lightness(full_lightness);
    let full = full_cold.mix(full_hot, mem_usage);

    let text_lightness_boost = remap(mem_usage,
                                     0.0,
                                     1.0,
                                     TEXT_LIGHTNESS_BOOST_NONE,
                                     TEXT_LIGHTNESS_BOOST_FULL);
    let text_lightness = full_lightness + text_lightness_boost;
    let text_cold = cold.set_lightness(text_lightness);
    let text_hot = hot.set_lightness(text_lightness);
    let text = text_cold.mix(text_hot, mem_usage);

    let percent = (100.0 * mem_usage) as i32;
    let usage = format!(" {:>4}M{:>3}% ", mem_used, percent);
    let width = usage.len() as i32;

    let (fully_filled_cells, partial_fill_rate) = quantize(mem_usage, width);
    let partial = none.mix(full, partial_fill_rate);

    let none = none.clamp_to_888();
    let full = full.clamp_to_888();
    let text = text.clamp_to_888();
    let partial = partial.clamp_to_888();

    let partition = zipper_split(&usage, fully_filled_cells as usize);
    let (full_cells, partial_cell, none_cells) = partition;
    let usage = format!("[{}:{}][{}:{}][{}:{}]",
                        full,
                        escape_fastup(full_cells),
                        partial,
                        escape_fastup(partial_cell),
                        none,
                        escape_fastup(none_cells));

    let node = format!("({}:{})", text, usage);
    parse_for_first_node(&node).unwrap()
}

fn escape_fastup<T>(input: T) -> String
    where T: ToString
{
    fastup::escape_for_text(&input.to_string())
}

/// Linearly remap `x` from inclusive range [`xa`, `xb`] to
/// inclusive range [`ra`, `rb`]. `x` must fall inside [`xa`, `xb`],
/// or panic will be issued.
fn remap<T>(x: T, xa: T, xb: T, ra: T, rb: T) -> T
    where T: PartialOrd + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + Copy
{
    assert!(xa <= x && x <= xb);
    (x - xa) * (rb - ra) / (xb - xa) + ra
}

/// Returns (quantized, remaining)
fn quantize(x: f32, steps: i32) -> (i32, f32) {
    let x = x * (steps as f32);
    let quantized = x.floor();
    let remaining = x - quantized;
    let quantized = quantized as i32;
    (quantized, remaining)
}

fn byte_index_from_char_index(s: &str, nchar: usize) -> usize {
    s.chars().take(nchar).map(|ch| ch.len_utf8()).sum()
}

fn split_at_char_index(s: &str, at: usize) -> (&str, &str) {
    let at = byte_index_from_char_index(s, at);
    s.split_at(at)
}

/// ```text
/// zipper_split("hello world", 3)
///                  ^--------- at = 3
///
/// Returns ("hel", "l", "o world")
/// ```
fn zipper_split(s: &str, at: usize) -> (&str, &str, &str) {
    let (first, rest) = split_at_char_index(s, at);
    let (elem, last) = split_at_char_index(rest, 1);
    (first, elem, last)
}

<|endoftext|>


<commit_before>use value::Value;

fn compiler_schema() -> Vec<(&'static str, Vec<&'static str>, Vec<&'static str>)> {
    // the schema is arranged as (table name, unique key fields, other fields)

    vec![
    // all state lives in a view of some kind
    // `kind` is one of:
    // "table" - a view which can depend on the past
    // "join" - take the product of multiple views and filter the results
    // "union" - take the union of multiple views
    // "aggregate" - group one view by the contents of another and run reducing functions on the groups
    // "primitive" - a built-in function, represented as a view with one or more non-Data fields
    ("view", vec!["view"], vec!["kind"]),

    // views have fields
    // some fields have constraints on how they can be queried
    // `kind` is one of:
    // "output" - a normal field
    // "scalar input" - a field that must be constrained to a single scalar value
    // "vector input" - a field that must be constrained to a single vector value (in an aggregate)
    ("field", vec!["view", "field"], vec!["kind"]),

    // source ids have two purposes
    // a) uniquely generated ids to disambiguate multiple uses of the same view
    // (eg when joining a view with itself)
    // b) fixed ids to identify views which are used for some specific purpose
    // (these are "insert" and "remove" in tables and "inner" and "outer" in aggregates)
    ("source", vec!["view", "source"], vec!["source view"]),

    // every view also has an implicit "constant" source
    // anywhere source-field is expected, you can instead use "constant"-id
    // `value` may be any valid eve value
    ("constant", vec!["constant"], vec!["value"]),

    // constraints belong to a join view
    // the left and right fields are compared using the operation
    // `operation` is one of "==", "/=", "<", "<=", ">", ">="
    ("constraint", vec!["constraint"], vec!["view"]),
    ("constraint left", vec!["constraint"], vec!["left source", "left field"]),
    ("constraint right", vec!["constraint"], vec!["right source", "right field"]),
    ("constraint operation", vec!["constraint"], vec!["operation"]),

    // aggregates group an "inner" source by the rows of an "outer" source
    // the grouping is determined by binding inner fields to outer fields or constants
    ("aggregate grouping", vec!["aggregate", "inner field"], vec!["group source", "group field"]),
    // before aggregation the groups are sorted
    // `priority` is an f64. higher priority fields are compared first. ties are broken by field id
    // `direction` is one of "ascending" or "descending"
    // fields which have no entry default to 0, "ascending"
    ("aggregate sorting", vec!["aggregate", "inner field"], vec!["priority", "direction"]),
    // groups may optionally be limited by an inner field or constant
    ("aggregate limit from", vec!["aggregate"], vec!["from source", "from field"]),
    ("aggregate limit to", vec!["aggregate"], vec!["to source", "to field"]),
    // the groups may be reduced by binding against reducer sources
    // constants and grouped inner fields may both be used as ScalarInput arguments
    // ungrouped inner fields which are not bound to outer fields may be used as VectorInput arguments
    ("aggregate argument", vec!["aggregate", "reducer source", "reducer field"], vec!["argument source", "argument field"]),

    // views produce output by binding fields from sources
    // each table or join field must be bound exactly once
    // each aggregate field must be bound exactly once and can only bind constants, inner fields or reducer outputs
    // each union field must be bound exactly once per source
    // (the unique key is different for union than for other kinds, so I don't give a key at all)
    ("select", vec![], vec!["view", "view field", "source", "source field"]),

    // things that live in an ordered list are sorted by some f64 (ties are broken by Id)
    // `priority` is an f64. higher priority things are displayed first. ties are broken by field id
    // things which have no entry default to 0
    ("display order", vec!["id"], vec!["priority"]),
    // things can have human readable names
    // `name` is a string
    ("display name", vec!["id"], vec!["name"]),
    ]
}<commit_msg>Fix field ordering<commit_after>use value::Value;

fn compiler_schema() -> Vec<(&'static str, Vec<&'static str>, Vec<&'static str>)> {
    // the schema is arranged as (table name, unique key fields, other fields)

    vec![
    // all state lives in a view of some kind
    // `kind` is one of:
    // "table" - a view which can depend on the past
    // "join" - take the product of multiple views and filter the results
    // "union" - take the union of multiple views
    // "aggregate" - group one view by the contents of another and run reducing functions on the groups
    // "primitive" - a built-in function, represented as a view with one or more non-Data fields
    ("view", vec!["view"], vec!["kind"]),

    // views have fields
    // some fields have constraints on how they can be queried
    // `kind` is one of:
    // "output" - a normal field
    // "scalar input" - a field that must be constrained to a single scalar value
    // "vector input" - a field that must be constrained to a single vector value (in an aggregate)
    ("field", vec!["view", "field"], vec!["kind"]),
    // fields are displayed in order
    // `priority` is an f64. higher priority fields are displayed first. ties are broken by field id
    ("field display order", vec!["view", "field"], vec!["priority"]),

    // source ids have two purposes
    // a) uniquely generated ids to disambiguate multiple uses of the same view
    // (eg when joining a view with itself)
    // b) fixed ids to identify views which are used for some specific purpose
    // (these are "insert" and "remove" in tables and "inner" and "outer" in aggregates)
    ("source", vec!["view", "source"], vec!["source view"]),

    // every view also has an implicit "constant" source
    // anywhere source-field is expected, you can instead use "constant"-id
    // `value` may be any valid eve value
    ("constant", vec!["constant"], vec!["value"]),

    // constraints belong to a join view
    // the left and right fields are compared using the operation
    // `operation` is one of "==", "/=", "<", "<=", ">", ">="
    ("constraint", vec!["constraint"], vec!["view"]),
    ("constraint left", vec!["constraint"], vec!["left source", "left field"]),
    ("constraint right", vec!["constraint"], vec!["right source", "right field"]),
    ("constraint operation", vec!["constraint"], vec!["operation"]),

    // aggregates group an "inner" source by the rows of an "outer" source
    // the grouping is determined by binding inner fields to outer fields or constants
    ("aggregate grouping", vec!["aggregate", "inner field"], vec!["group source", "group field"]),
    // before aggregation the groups are sorted
    // `priority` is an f64. higher priority fields are compared first. ties are broken by field id
    // `direction` is one of "ascending" or "descending"
    // fields which have no entry default to 0, "ascending"
    ("aggregate sorting", vec!["aggregate", "inner field"], vec!["priority", "direction"]),
    // groups may optionally be limited by an inner field or constant
    ("aggregate limit from", vec!["aggregate"], vec!["from source", "from field"]),
    ("aggregate limit to", vec!["aggregate"], vec!["to source", "to field"]),
    // the groups may be reduced by binding against reducer sources
    // constants and grouped inner fields may both be used as ScalarInput arguments
    // ungrouped inner fields which are not bound to outer fields may be used as VectorInput arguments
    ("aggregate argument", vec!["aggregate", "reducer source", "reducer field"], vec!["argument source", "argument field"]),

    // views produce output by binding fields from sources
    // each table or join field must be bound exactly once
    // each aggregate field must be bound exactly once and can only bind constants, inner fields or reducer outputs
    // each union field must be bound exactly once per source
    // (the unique key is different for union than for other kinds, so I don't give a key at all)
    ("select", vec![], vec!["view", "view field", "source", "source field"]),

    // things can have human readable names
    // `name` is a string
    ("display name", vec!["id"], vec!["name"]),
    ]
}<|endoftext|>


<commit_before>use ::cairo;
use ::glib::translate::*;
use ::glib_sys;
use ::libc;

use std::f64::consts::*;

use cairo::MatrixTrait;
use cssparser::{Parser, ParserInput, Token, ParseError as CssParseError};

use error::*;
use parsers::{Parse, ParseError, optional_comma};

impl Parse for cairo::Matrix {
    type Data = ();
    type Err = AttributeError;

    fn parse (s: &str, _: ()) -> Result<cairo::Matrix, AttributeError> {
        parse_transform (s)
    }
}

pub fn parse_transform(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let matrix = parse_transform_list(s)?;

    matrix.try_invert ().map (|_| matrix)
        .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
}

fn parse_transform_list(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let mut matrix = cairo::Matrix::identity();

    loop {
        let m = parse_transform_command(&mut parser)?;
        matrix = cairo::Matrix::multiply(&m, &matrix);

        if parser.is_exhausted() {
            break;
        }

        optional_comma(&mut parser);
    }

    Ok(matrix)
}

fn make_expected_function_error() -> AttributeError {
    AttributeError::from(ParseError::new("expected matrix|translate|scale|rotate|skewX|skewY"))
}

fn parse_transform_command(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match parser.next()?.clone() {
        Token::Function(ref name) => parse_transform_function(name, parser),

        Token::Ident(ref name) => {
            parser.expect_parenthesis_block()?;
            parse_transform_function(name, parser)
        },

        _ => Err(make_expected_function_error()),
    }
}

fn parse_transform_function(name: &str, parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match name {
        "matrix"    => parse_matrix_args(parser),
        "translate" => parse_translate_args(parser),
        "scale"     => parse_scale_args(parser),
        "rotate"    => parse_rotate_args(parser),
        "skewX"     => parse_skewx_args(parser),
        "skewY"     => parse_skewy_args(parser),
        _           => Err(make_expected_function_error()),
    }
}

fn parse_matrix_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let xx = p.expect_number()? as f64;
        optional_comma(p);

        let yx = p.expect_number()? as f64;
        optional_comma(p);

        let xy = p.expect_number()? as f64;
        optional_comma(p);

        let yy = p.expect_number()? as f64;
        optional_comma(p);

        let x0 = p.expect_number()? as f64;
        optional_comma(p);

        let y0 = p.expect_number()? as f64;

        Ok(cairo::Matrix::new(xx, yx, xy, yy, x0, y0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_translate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let tx = p.expect_number()?;

        optional_comma(p);

        let ty = p.expect_number()?;

        Ok(cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx as f64, ty as f64))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_scale_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let x = p.expect_number()?;

        let y = p.try(|p| -> Result<f32, CssParseError<()>> {
            optional_comma(p);
            Ok(p.expect_number()?)
        }).unwrap_or(x);

        Ok(cairo::Matrix::new(x as f64, 0.0, 0.0, y as f64, 0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_rotate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let angle = p.expect_number()? as f64 * PI / 180.0;
        let (s, c) = angle.sin_cos();

        let (tx, ty) = p.try(|p| -> Result<_, CssParseError<()>> {
            optional_comma(p);
            let tx = p.expect_number()? as f64;

            optional_comma(p);
            let ty = p.expect_number()? as f64;

            Ok((tx, ty))
        }).unwrap_or((0.0, 0.0));

        let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

        m = cairo::Matrix::multiply (&cairo::Matrix::new (c, s, -s, c, 0.0, 0.0), &m);
        m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
        Ok(m)
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_skewx_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let a = p.expect_number()? as f64 * PI / 180.0;
        Ok(cairo::Matrix::new (1.0,      0.0,
                               a.tan (), 1.0,
                               0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_skewy_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let a = p.expect_number()? as f64 * PI / 180.0;
        Ok(cairo::Matrix::new (1.0,  a.tan (),
                               0.0,  1.0,
                               0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

/*
pub fn parse_transform (s: &str) -> Result <cairo::Matrix, AttributeError> {
    let r = parse_TransformList (s);

    match r {
        Ok (m) => {
            m.try_invert ().map (|_| m)
                .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
        },

        Err (e) => {
            Err (AttributeError::Parse (ParseError::new (format! ("{:?}", e))))
        }
    }
}
*/

#[cfg(test)]
fn make_rotation_matrix (angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
    let angle = angle_degrees * PI / 180.0;

    let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

    let mut r = cairo::Matrix::identity ();
    r.rotate (angle);
    m = cairo::Matrix::multiply (&r, &m);

    m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
    m
}

#[no_mangle]
pub extern fn rsvg_parse_transform (out_matrix: *mut cairo::Matrix, s: *const libc::c_char) -> glib_sys::gboolean {
    assert! (!out_matrix.is_null ());
    assert! (!s.is_null ());

    let string = unsafe { String::from_glib_none (s) };
    let matrix: &mut cairo::Matrix = unsafe { &mut *out_matrix };

    match parse_transform (&string) {
        Ok (m) => {
            *matrix = m;
            true.to_glib ()
        },

        Err (_) => {
            *matrix = cairo::Matrix::identity ();
            false.to_glib ()
        }
    }
}

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

    #[test]
    #[ignore]
    fn parses_valid_transform () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }

    fn assert_parse_error(s: &str) {
        match parse_transform(s) {
            Err(AttributeError::Parse(_)) => {},
            _ => { panic!(); }
        }
    }

    #[test]
    #[ignore]
    fn syntax_error_yields_parse_error () {
        assert_parse_error("foo");
        assert_parse_error("matrix (1 2 3 4 5)");
        assert_parse_error("translate(1 2 3 4 5)");
        assert_parse_error("translate (1,)");
    }

    #[test]
    #[ignore]
    fn invalid_transform_yields_value_error () {
        match parse_transform ("matrix (0 0 0 0 0 0)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), translate (10, 10)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), skewX (90)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }
    }
}

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

    #[test]
    fn parses_matrix () {
        assert_eq! (parse_transform ("matrix (1 2 3 4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix(1,2,3,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix (1,2.25,-3.25e2,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.25, -325.0, 4.0, 5.0, 6.0));
    }

    #[test]
    fn parses_translate () {
        assert_eq! (parse_transform ("translate(-1 -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        // assert_eq! (parse_transform ("translate(-1, -2)").unwrap (),
        //             cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        // assert_eq! (parse_transform ("translate(-1)").unwrap (),
        //             cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, 0.0));
    }

    #[test]
    fn parses_scale() {
        assert_eq! (parse_transform ("scale (-1)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -1.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1 -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1, -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));
    }

    #[test]
    fn parses_rotate () {
        assert_eq! (parse_transform ("rotate (30)").unwrap (), make_rotation_matrix (30.0, 0.0, 0.0));
        assert_eq! (parse_transform ("rotate (30,-1,-2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
        assert_eq! (parse_transform ("rotate(30, -1, -2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
    }

    fn make_skew_x_matrix (angle_degrees: f64) -> cairo::Matrix {
        let a = angle_degrees * PI / 180.0;
        cairo::Matrix::new (1.0,      0.0,
                            a.tan (), 1.0,
                            0.0, 0.0)
    }

    fn make_skew_y_matrix (angle_degrees: f64) -> cairo::Matrix {
        let mut m = make_skew_x_matrix (angle_degrees);
        m.yx = m.xy;
        m.xy = 0.0;
        m
    }

    #[test]
    fn parses_skew_x () {
        assert_eq! (parse_transform ("skewX (30)").unwrap (), make_skew_x_matrix (30.0));
    }

    #[test]
    fn parses_skew_y () {
        assert_eq! (parse_transform ("skewY (30)").unwrap (), make_skew_y_matrix (30.0));
    }

    #[test]
    #[ignore]
    fn parses_transform_list () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        assert_eq! (parse_transform ("scale(10)rotate(30, 10, 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &s));

        assert_eq! (parse_transform ("translate(20, 30), scale (10)").unwrap (),
                    cairo::Matrix::multiply (&s, &t));

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }
}
<commit_msg>Enable all the remaining transform tests<commit_after>use ::cairo;
use ::glib::translate::*;
use ::glib_sys;
use ::libc;

use std::f64::consts::*;

use cairo::MatrixTrait;
use cssparser::{Parser, ParserInput, Token, ParseError as CssParseError};

use error::*;
use parsers::{Parse, ParseError, optional_comma};

impl Parse for cairo::Matrix {
    type Data = ();
    type Err = AttributeError;

    fn parse (s: &str, _: ()) -> Result<cairo::Matrix, AttributeError> {
        parse_transform (s)
    }
}

pub fn parse_transform(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let matrix = parse_transform_list(s)?;

    matrix.try_invert ().map (|_| matrix)
        .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
}

fn parse_transform_list(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let mut matrix = cairo::Matrix::identity();

    loop {
        let m = parse_transform_command(&mut parser)?;
        matrix = cairo::Matrix::multiply(&m, &matrix);

        if parser.is_exhausted() {
            break;
        }

        optional_comma(&mut parser);
    }

    Ok(matrix)
}

fn make_expected_function_error() -> AttributeError {
    AttributeError::from(ParseError::new("expected matrix|translate|scale|rotate|skewX|skewY"))
}

fn parse_transform_command(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match parser.next()?.clone() {
        Token::Function(ref name) => parse_transform_function(name, parser),

        Token::Ident(ref name) => {
            parser.expect_parenthesis_block()?;
            parse_transform_function(name, parser)
        },

        _ => Err(make_expected_function_error()),
    }
}

fn parse_transform_function(name: &str, parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match name {
        "matrix"    => parse_matrix_args(parser),
        "translate" => parse_translate_args(parser),
        "scale"     => parse_scale_args(parser),
        "rotate"    => parse_rotate_args(parser),
        "skewX"     => parse_skewx_args(parser),
        "skewY"     => parse_skewy_args(parser),
        _           => Err(make_expected_function_error()),
    }
}

fn parse_matrix_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let xx = p.expect_number()? as f64;
        optional_comma(p);

        let yx = p.expect_number()? as f64;
        optional_comma(p);

        let xy = p.expect_number()? as f64;
        optional_comma(p);

        let yy = p.expect_number()? as f64;
        optional_comma(p);

        let x0 = p.expect_number()? as f64;
        optional_comma(p);

        let y0 = p.expect_number()? as f64;

        Ok(cairo::Matrix::new(xx, yx, xy, yy, x0, y0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_translate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let tx = p.expect_number()?;

        optional_comma(p);

        let ty = p.expect_number()?;

        Ok(cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx as f64, ty as f64))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_scale_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let x = p.expect_number()?;

        let y = p.try(|p| -> Result<f32, CssParseError<()>> {
            optional_comma(p);
            Ok(p.expect_number()?)
        }).unwrap_or(x);

        Ok(cairo::Matrix::new(x as f64, 0.0, 0.0, y as f64, 0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_rotate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let angle = p.expect_number()? as f64 * PI / 180.0;
        let (s, c) = angle.sin_cos();

        let (tx, ty) = p.try(|p| -> Result<_, CssParseError<()>> {
            optional_comma(p);
            let tx = p.expect_number()? as f64;

            optional_comma(p);
            let ty = p.expect_number()? as f64;

            Ok((tx, ty))
        }).unwrap_or((0.0, 0.0));

        let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

        m = cairo::Matrix::multiply (&cairo::Matrix::new (c, s, -s, c, 0.0, 0.0), &m);
        m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
        Ok(m)
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_skewx_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let a = p.expect_number()? as f64 * PI / 180.0;
        Ok(cairo::Matrix::new (1.0,      0.0,
                               a.tan (), 1.0,
                               0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_skewy_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let a = p.expect_number()? as f64 * PI / 180.0;
        Ok(cairo::Matrix::new (1.0,  a.tan (),
                               0.0,  1.0,
                               0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

/*
pub fn parse_transform (s: &str) -> Result <cairo::Matrix, AttributeError> {
    let r = parse_TransformList (s);

    match r {
        Ok (m) => {
            m.try_invert ().map (|_| m)
                .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
        },

        Err (e) => {
            Err (AttributeError::Parse (ParseError::new (format! ("{:?}", e))))
        }
    }
}
*/

#[cfg(test)]
fn make_rotation_matrix (angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
    let angle = angle_degrees * PI / 180.0;

    let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

    let mut r = cairo::Matrix::identity ();
    r.rotate (angle);
    m = cairo::Matrix::multiply (&r, &m);

    m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
    m
}

#[no_mangle]
pub extern fn rsvg_parse_transform (out_matrix: *mut cairo::Matrix, s: *const libc::c_char) -> glib_sys::gboolean {
    assert! (!out_matrix.is_null ());
    assert! (!s.is_null ());

    let string = unsafe { String::from_glib_none (s) };
    let matrix: &mut cairo::Matrix = unsafe { &mut *out_matrix };

    match parse_transform (&string) {
        Ok (m) => {
            *matrix = m;
            true.to_glib ()
        },

        Err (_) => {
            *matrix = cairo::Matrix::identity ();
            false.to_glib ()
        }
    }
}

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

    #[test]
    fn parses_valid_transform () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }

    fn assert_parse_error(s: &str) {
        match parse_transform(s) {
            Err(AttributeError::Parse(_)) => {},
            _ => { panic!(); }
        }
    }

    #[test]
    fn syntax_error_yields_parse_error () {
        assert_parse_error("foo");
        assert_parse_error("matrix (1 2 3 4 5)");
        assert_parse_error("translate(1 2 3 4 5)");
        assert_parse_error("translate (1,)");
        assert_parse_error("scale (1,)");
        assert_parse_error("skewX (1,2)");
        assert_parse_error("skewY ()");
        assert_parse_error("skewY");
    }

    #[test]
    fn invalid_transform_yields_value_error () {
        match parse_transform ("matrix (0 0 0 0 0 0)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), translate (10, 10)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), skewX (90)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }
    }
}

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

    #[test]
    fn parses_matrix () {
        assert_eq! (parse_transform ("matrix (1 2 3 4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix(1,2,3,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix (1,2.25,-3.25e2,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.25, -325.0, 4.0, 5.0, 6.0));
    }

    #[test]
    fn parses_translate () {
        assert_eq! (parse_transform ("translate(-1 -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        // assert_eq! (parse_transform ("translate(-1, -2)").unwrap (),
        //             cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        // assert_eq! (parse_transform ("translate(-1)").unwrap (),
        //             cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, 0.0));
    }

    #[test]
    fn parses_scale() {
        assert_eq! (parse_transform ("scale (-1)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -1.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1 -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1, -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));
    }

    #[test]
    fn parses_rotate () {
        assert_eq! (parse_transform ("rotate (30)").unwrap (), make_rotation_matrix (30.0, 0.0, 0.0));
        assert_eq! (parse_transform ("rotate (30,-1,-2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
        assert_eq! (parse_transform ("rotate(30, -1, -2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
    }

    fn make_skew_x_matrix (angle_degrees: f64) -> cairo::Matrix {
        let a = angle_degrees * PI / 180.0;
        cairo::Matrix::new (1.0,      0.0,
                            a.tan (), 1.0,
                            0.0, 0.0)
    }

    fn make_skew_y_matrix (angle_degrees: f64) -> cairo::Matrix {
        let mut m = make_skew_x_matrix (angle_degrees);
        m.yx = m.xy;
        m.xy = 0.0;
        m
    }

    #[test]
    fn parses_skew_x () {
        assert_eq! (parse_transform ("skewX (30)").unwrap (), make_skew_x_matrix (30.0));
    }

    #[test]
    fn parses_skew_y () {
        assert_eq! (parse_transform ("skewY (30)").unwrap (), make_skew_y_matrix (30.0));
    }

    #[test]
    fn parses_transform_list () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        assert_eq! (parse_transform ("scale(10)rotate(30, 10, 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &s));

        assert_eq! (parse_transform ("translate(20, 30), scale (10)").unwrap (),
                    cairo::Matrix::multiply (&s, &t));

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }
}
<|endoftext|>


<commit_before>use base::{
    AbstractCFType,
    AbstractCFTypeRef,
    Boolean,
    CFAllocatorRef,
    CFIndex,
    CFRelease,
    CFTypeRef,
};
use libc::c_char;

pub type CFSetRetainCallBack = *u8;
pub type CFSetReleaseCallBack = *u8;
pub type CFSetCopyDescriptionCallBack = *u8;
pub type CFSetEqualCallBack = *u8;
pub type CFSetHashCallBack = *u8;

pub struct CFSetCallBacks {
    version: CFIndex,
    retain: CFSetRetainCallBack,
    release: CFSetReleaseCallBack,
    copyDescription: CFSetCopyDescriptionCallBack,
    equal: CFSetEqualCallBack,
    hash: CFSetHashCallBack,
}

struct __CFSet { private: () }
pub type CFSetRef = *__CFSet;

impl CFSetRef : AbstractCFTypeRef {
    pure fn as_type_ref(&self) -> CFTypeRef { *self as CFTypeRef }
}

pub struct CFSet {
    obj: CFSetRef,

    drop {
        unsafe {
            CFRelease(cast::transmute(self.obj));
        }
    }
}

pub impl CFSet : AbstractCFType<CFSetRef> {
    pure fn get_ref() -> CFSetRef { self.obj }

    static fn wrap(obj: CFSetRef) -> CFSet {
        CFSet { obj: obj }
    }

    static fn unwrap(wrapper: CFSet) -> CFSetRef {
        wrapper.obj
    }
}

#[link_args="-framework CoreFoundation"]
#[nolink]
extern {
    /*
     * CFSet.h
     */

    const kCFTypeSetCallBacks: CFSetCallBacks;
    const kCFTypeCopyStringSetCallBacks: CFSetCallBacks;

    //fn CFSetCreate
    //fn CFSetCreateCopy
    //fn CFSetContainsValue
    //fn CFSetGetCount
    //fn CFSetGetCountOfValue
    //fn CFSetGetValue
    //fn CFSetGetValueIfPresent
    //fn CFSetGetValues
    //fn CFSetApplyFunction
    //fn CFSetGetTypeID
}

<commit_msg>Add bindings for most CFSet methods, and static new() for the wrapper.<commit_after>use base::{
    AbstractCFType,
    AbstractCFTypeRef,
    Boolean,
    CFAllocatorRef,
    CFIndex,
    CFRelease,
    CFTypeRef,
    CFTypeID,
    kCFAllocatorDefault,
};
use libc::{c_char, c_void};

pub type CFSetRetainCallBack = *u8;
pub type CFSetReleaseCallBack = *u8;
pub type CFSetCopyDescriptionCallBack = *u8;
pub type CFSetEqualCallBack = *u8;
pub type CFSetHashCallBack = *u8;

pub struct CFSetCallBacks {
    version: CFIndex,
    retain: CFSetRetainCallBack,
    release: CFSetReleaseCallBack,
    copyDescription: CFSetCopyDescriptionCallBack,
    equal: CFSetEqualCallBack,
    hash: CFSetHashCallBack,
}

struct __CFSet { private: () }
pub type CFSetRef = *__CFSet;

impl CFSetRef : AbstractCFTypeRef {
    pure fn as_type_ref(&self) -> CFTypeRef { *self as CFTypeRef }
}

struct CFSet<ElemRefType : AbstractCFTypeRef,
             ElemType    : AbstractCFType<ElemRefType>> {
    obj: CFSetRef,

    drop {
        unsafe {
            CFRelease(cast::transmute(self.obj));
        }
    }
}

pub impl<ElemRefType : AbstractCFTypeRef,
         ElemType    : AbstractCFType<ElemRefType>>
    CFSet<ElemRefType, ElemType> {
    static fn new(elems: &[ElemType]) -> CFSet<ElemRefType, ElemType> {
        let set_ref : CFSetRef;
        let elems_refs = do vec::map(elems) |e: &ElemType| { e.get_ref().as_type_ref() };

        unsafe {
            set_ref = CFSetCreate(kCFAllocatorDefault,
                                  cast::transmute(vec::raw::to_ptr(elems_refs)),
                                  elems.len() as CFIndex,
                                  ptr::to_unsafe_ptr(&kCFTypeSetCallBacks));
        }
        // return CFSet::wrap(set_ref)
        return CFSet { obj: set_ref };
    }
}

pub impl<ElemRefType : AbstractCFTypeRef,
         ElemType    : AbstractCFType<ElemRefType>>
    CFSet<ElemRefType, ElemType> : AbstractCFType<CFSetRef> {
    pure fn get_ref() -> CFSetRef { self.obj }

    static fn wrap(obj: CFSetRef) -> CFSet<ElemRefType, ElemType> {
        CFSet { obj: obj }
    }

    static fn unwrap(wrapper: CFSet<ElemRefType, ElemType>) -> CFSetRef {
        wrapper.obj
    }
}

#[link_args="-framework CoreFoundation"]
#[nolink]
extern {
    /*
     * CFSet.h
     */

    const kCFTypeSetCallBacks: CFSetCallBacks;
    const kCFTypeCopyStringSetCallBacks: CFSetCallBacks;

    /* Creating Sets */
    fn CFSetCreate(allocator: CFAllocatorRef, values: **c_void, numValues: CFIndex,
                   callBacks: *CFSetCallBacks) -> CFSetRef;
    fn CFSetCreateCopy(allocator: CFAllocatorRef, theSet: CFSetRef) -> CFSetRef;

    /* Examining a Set */
    fn CFSetContainsValue(theSet: CFSetRef, value: *c_void) -> Boolean;
    fn CFSetGetCount(theSet: CFSetRef) -> CFIndex;
    fn CFSetGetCountOfValue(theSet: CFSetRef, value: *c_void) -> CFIndex;
    fn CFSetGetValue(theSet: CFSetRef, value: *c_void) -> *c_void;
    fn CFSetGetValueIfPresent(theSet: CFSetRef, candidate: *c_void, value: **c_void) -> Boolean;
    fn CFSetGetValues(theSet: CFSetRef, values: **c_void);

    /* Applying a Function to Set Members */
    //fn CFSetApplyFunction

    fn CFSetGetTypeID() -> CFTypeID;
}

<|endoftext|>


<commit_before>extern crate time;

use std::rand::{Rng, task_rng};
use std::collections::HashMap;
use time::precise_time_ns;

pub type Node = uint;

#[deriving(Show)]
pub struct NodePt {
    id: Node,
    x: f64,
    y: f64
}

impl NodePt {
	fn new(nodeId: Node, x: f64, y: f64) -> NodePt {
		NodePt {
			id: nodeId,
			x: x,
			y: y
		}
	}

	fn distance_to(self, other: NodePt) -> f64 {
		let xx = (self.x - other.x) * (self.x - other.x);
		let yy = (self.y - other.y) * (self.y - other.y);

		(xx+yy).sqrt()
	}
}

impl PartialEq for NodePt {
	fn eq(&self, other: &NodePt) -> bool {
		self.id == other.id
	}

	fn ne(&self, other: &NodePt) -> bool {
		self.id != other.id
	}
}

#[deriving(Show, Clone)]
pub struct Edge {
	from: Node,
	to: Node,
	weight: f64
}

impl Edge {
	fn new(a: NodePt, b: NodePt) -> Edge {
		Edge {
			from: a.id,
			to: b.id,
			weight: a.distance_to(b)
		}
	}
}

#[deriving(Show)]
pub struct Tour {
    nodes: Vec<Node>,
    total_weight: f64
}

impl Tour {
	fn new(nodes: Vec<Node>, weight: f64) -> Tour {
		Tour {
			nodes: nodes,
			total_weight: weight
		}
	}

	fn calc_tour_weight(tour: &Vec<Node>, graph: &Graph) -> f64 {
		let mut tour_weight = 0.0;
		let mut last_node = tour.get(0u);
		for node in tour.iter() {
			tour_weight += graph.get(*last_node, *node);
			last_node = node;
		}

		tour_weight
	}

	fn random_tour<R: Rng>(rng: &mut R, graph: &Graph) -> Tour {
		let node_count = graph.adj_list.len();
		let mut tour_nodes: Vec<Node> = range(0, node_count).collect();
		rng.shuffle(tour_nodes.as_mut_slice());
		let tour_weight = Tour::calc_tour_weight(&tour_nodes, graph);

		Tour::new(tour_nodes, tour_weight)
	}

}

#[deriving(Show)]
pub struct Graph {
	adj_list: HashMap<Node, Vec<Edge>>
}

impl Graph {
	fn new(adj_list: HashMap<Node, Vec<Edge>>) -> Graph {
		Graph {
			adj_list: adj_list
		}
	}

	fn from_nodes(nodes: Vec<NodePt>) -> Graph {
		let mut map: HashMap<Node, Vec<Edge>> = HashMap::new();
		for i in range(0, nodes.len()) {
			let mut adj_edges: Vec<Edge> = Vec::new();

			for j in range(0, nodes.len()) {
				let a = nodes.get(i);
				let b = nodes.get(j);
				if i != j {
					adj_edges.push(Edge::new(*a, *b));
				}
			}

			map.insert(i, adj_edges);
		}

		Graph::new(map)
	}

	fn random_graph<R: Rng>(rng: &mut R, num_nodes: uint, x_max: f64, y_max: f64) -> Graph {
		let points = rng.gen_iter::<(f64, f64)>()
			.enumerate()
			.map(|t|
				match t { (idx, (x, y)) => NodePt::new(idx, x * x_max, y * y_max) })
			.take(num_nodes)
			.collect();

		Graph::from_nodes(points)
	}

	fn get(&self, n: Node, m: Node) -> f64 {
		if n == m {
			0.0
		}
		else {
			let edges = self.adj_list.get(&n);
			let mut weight = 0.0;

			for edge in edges.iter() {
				if edge.to == m {
					weight = edge.weight;
				}
			}
			weight
		}
	}

}

fn main() {
	let mut rng = task_rng();
	let node_count = 4u;
	let iters = 10000;
	let graph = Graph::random_graph(&mut rng, node_count, 200.0, 200.0);

	let t0 = time::precise_time_ns();
	for _ in range(0, iters) {
		let tour = Tour::random_tour(&mut rng, &graph);
	}
	let t1 = time::precise_time_ns();

	let dt = (t1 - t0) as f64;
	println!("{} iters: t = {} ms", iters, (dt / 1e6))
}<commit_msg>started on read_from_file<commit_after>extern crate time;

use std::rand::{Rng, task_rng};
use std::collections::HashMap;
use time::precise_time_ns;
use std::io::{File, BufferedReader, IoResult};

pub type Node = uint;

#[deriving(Show)]
pub struct NodePt {
    id: Node,
    x: f64,
    y: f64
}

impl NodePt {
	fn new(nodeId: Node, x: f64, y: f64) -> NodePt {
		NodePt {
			id: nodeId,
			x: x,
			y: y
		}
	}

	fn distance_to(self, other: NodePt) -> f64 {
		let xx = (self.x - other.x) * (self.x - other.x);
		let yy = (self.y - other.y) * (self.y - other.y);

		(xx+yy).sqrt()
	}
}

impl PartialEq for NodePt {
	fn eq(&self, other: &NodePt) -> bool {
		self.id == other.id
	}

	fn ne(&self, other: &NodePt) -> bool {
		self.id != other.id
	}
}

#[deriving(Show, Clone)]
pub struct Edge {
	from: Node,
	to: Node,
	weight: f64
}

impl Edge {
	fn new(a: NodePt, b: NodePt) -> Edge {
		Edge {
			from: a.id,
			to: b.id,
			weight: a.distance_to(b)
		}
	}
}

#[deriving(Show)]
pub struct Tour {
    nodes: Vec<Node>,
    total_weight: f64,
    fitness: f64
}

impl Tour {
	fn new(nodes: Vec<Node>, weight: f64) -> Tour {
		Tour {
			nodes: nodes,
			total_weight: weight,
			fitness: 1.0 / weight
		}
	}

	fn calc_tour_weight(tour: &Vec<Node>, graph: &Graph) -> f64 {
		let mut tour_weight = 0.0;
		let mut last_node = tour.get(0u);
		for node in tour.iter() {
			tour_weight += graph.get(*last_node, *node);
			last_node = node;
		}

		tour_weight
	}

	fn random_tour<R: Rng>(rng: &mut R, graph: &Graph) -> Tour {
		let node_count = graph.adj_list.len();
		let mut tour_nodes: Vec<Node> = range(0, node_count).collect();
		rng.shuffle(tour_nodes.as_mut_slice());
		let last: Node = *tour_nodes.last().unwrap();
		tour_nodes.insert(0, last);
		let tour_weight = Tour::calc_tour_weight(&tour_nodes, graph);
		Tour::new(tour_nodes, tour_weight)
	}

}

#[deriving(Show)]
pub struct Graph {
	adj_list: HashMap<Node, Vec<Edge>>
}

impl Graph {
	fn new(adj_list: HashMap<Node, Vec<Edge>>) -> Graph {
		Graph {
			adj_list: adj_list
		}
	}

	fn from_nodes(nodes: Vec<NodePt>) -> Graph {
		let mut map: HashMap<Node, Vec<Edge>> = HashMap::new();
		for i in range(0, nodes.len()) {
			let mut adj_edges: Vec<Edge> = Vec::new();

			for j in range(0, nodes.len()) {
				let a = nodes.get(i);
				let b = nodes.get(j);
				if i != j {
					adj_edges.push(Edge::new(*a, *b));
				}
			}

			map.insert(i, adj_edges);
		}

		Graph::new(map)
	}

	fn random_graph<R: Rng>(rng: &mut R, num_nodes: uint, x_max: f64, y_max: f64) -> Graph {
		let points = rng.gen_iter::<(f64, f64)>()
			.enumerate()
			.map(|(idx, (x, y))| NodePt::new(idx, x * x_max, y * y_max))
			.take(num_nodes)
			.collect();

		Graph::from_nodes(points)
	}

	fn get(&self, n: Node, m: Node) -> f64 {
		if n == m {
			0.0
		}
		else {
			let edges = self.adj_list.get(&n);
			let result = edges.iter().filter(|edge| edge.to == m).nth(0);
			match result {
				Some(edge) => edge.weight,
				None => 0.0
			}
		}
	}

	fn from_file(file_path: &str) -> Graph {
		let path = Path::new(file_path);
		let mut file = BufferedReader::new(File::open(&path));
		let mut nodes: Vec<NodePt> = Vec::new();
		let mut i = 0u;

		for line in file.lines() {
			// let val: Vec<f64> = line.map(|l| {
			// 	l.as_slice().split(' ').map(|i| from_str::<f64>(i)).collect()
			// }).unwrap();

			// let nodePt = NodePt::new(i, *val.get(0), *val.get(1));
			line.map(|s| s.as_slice());
			i += 1;
		}

		Graph::from_nodes(nodes)
	}
}

fn main() {
	let test = "20 14";
	let split: Vec<f64> = test.split(' ').map(|i| from_str::<f64>(i).unwrap_or(0.0)).collect();
	println!("{}", NodePt::new(0, *split.get(0), *split.get(1)))

	let mut rng = task_rng();
	let graph = Graph::random_graph(&mut rng, 10, 200.0, 200.0);
}<|endoftext|>


<commit_before>extern crate env_logger;

extern crate gears;
use gears::dsl::command::*;
use gears::structure::model::Model;
use gears::structure::domain::Domain;

#[test]
fn test_dsl_tokens_to_tree() {
    let _ = env_logger::init();

    let tokens = vec![
        DslToken::With("domain".to_owned()),
        DslToken::BlockOpen,

        DslToken::With("entity post".to_owned()),
        DslToken::BlockOpen,

        DslToken::With("attibute name".to_owned()),
        DslToken::BlockOpen,

        DslToken::Comment("This adds a default value of 'zork'".to_owned()),
        DslToken::Command("add default 'zork'".to_owned()),

        DslToken::BlockClose,
        DslToken::BlockClose,
        DslToken::BlockClose,
    ];

    match tokens_as_tree(&tokens) {
        Ok(tree) => {
            assert_eq!(tree.len(), 1);
            match tree[0] {
                DslTree::Scope(ref e, ref v) => assert_eq!(e, "domain"),
                _ => assert!(false),

            }
        }
        Err(_) => {
            assert!(false);
        }
    }
}

#[test]
fn test_dsl_domain() {
    let _ = env_logger::init();

    let mut domain = Domain::default();
    let e_count = domain.entities.len();
    domain.interpret_dsl(&"add entity abc;");
    assert_eq!(domain.entities.len(), e_count + 1);

    let dsl = domain.generate_dsl();
    assert_eq!(dsl.len(), 1);
}

#[test]
fn test_dsl_domain_multiple_commands() {
    let _ = env_logger::init();

    let mut domain = Domain::default();

    let e_count = domain.entities.len();

    domain.interpret_dsl(&"add entity abc; remove entity abc; add entity post;");
    assert_eq!(domain.entities.len(), e_count + 1);

    let dsl = domain.generate_dsl();
    assert_eq!(dsl.len(), 1);

}

#[test]
fn test_dsl_domain_generate_and_consume() {
    let _ = env_logger::init();

    let mut domain = Domain::default();

    let e_count = domain.entities.len();

    match domain.interpret_dsl(&"add entity abc; remove entity abc; add entity post;") {
        Ok(()) => {
            assert!(true);
        }
        Err(_) => {
            assert!(false);
        }
    }

    let script = domain.to_text_dsl();

    let mut next_domain = Domain::default();
    match next_domain.interpret_dsl(&script) {
        Ok(()) => {
            assert!(true);
        }
        Err(_) => {
            assert!(false);
        }
    }

    assert_eq!(domain, next_domain);

}

#[test]
fn test_dsl_model_interpret() {
    let _ = env_logger::init();

    let mut model = Model::default();
    assert_eq!(model.domain.doc.entities.len(), 0);

    let _ = model.interpret_dsl(&"with domain { add entity zork; };");
    assert_eq!(model.domain.doc.entities.len(), 1);

    let _ = model.interpret_dsl(&"with domain { remove entity zork; };");
    assert_eq!(model.domain.doc.entities.len(), 0);

}

#[test]
fn test_dsl_model_interpret_multiline() {
    let _ = env_logger::init();

    let mut model = Model::default();
    assert_eq!(model.domain.doc.entities.len(), 0);

    let _ = model.interpret_dsl(
        &r#"with domain { add entity zork; add entity bork; add entity fnord; };"#,
    );
    assert_eq!(model.domain.doc.entities.len(), 3);

    let _ = model.interpret_dsl(
        &r#"with domain { remove entity zork; remove entity bork; };"#,
    );
    assert_eq!(model.domain.doc.entities.len(), 1);

}
<commit_msg>test: Clean up dsl tests<commit_after>extern crate env_logger;

extern crate gears;
use gears::dsl::command::*;
use gears::structure::model::Model;
use gears::structure::domain::Domain;

#[test]
fn test_dsl_tokens_to_tree() {
    let _ = env_logger::init();

    let tokens = vec![
        DslToken::With("domain".to_owned()),
        DslToken::BlockOpen,

        DslToken::With("entity post".to_owned()),
        DslToken::BlockOpen,

        DslToken::With("attibute name".to_owned()),
        DslToken::BlockOpen,

        DslToken::Comment("This adds a default value of 'zork'".to_owned()),
        DslToken::Command("add default 'zork'".to_owned()),

        DslToken::BlockClose,
        DslToken::BlockClose,
        DslToken::BlockClose,
    ];

    match tokens_as_tree(&tokens) {
        Ok(tree) => {
            assert_eq!(tree.len(), 1);
            match tree[0] {
                DslTree::Scope(ref e, ref v) => assert_eq!(e, "domain"),
                _ => assert!(false),

            }
        }
        Err(_) => {
            assert!(false);
        }
    }
}

#[test]
fn test_dsl_domain() {
    let _ = env_logger::init();

    let mut domain = Domain::default();
    let e_count = domain.entities.len();
    domain.interpret_dsl(&"add entity abc;");
    assert_eq!(domain.entities.len(), e_count + 1);

    let dsl = domain.generate_dsl();
    assert_eq!(dsl.len(), 1);
}

#[test]
fn test_dsl_domain_multiple_commands() {
    let _ = env_logger::init();

    let mut domain = Domain::default();

    let e_count = domain.entities.len();

    domain.interpret_dsl(&"add entity abc; remove entity abc; add entity post;");
    assert_eq!(domain.entities.len(), e_count + 1);

    let dsl = domain.generate_dsl();
    assert_eq!(dsl.len(), 1);

}

#[test]
fn test_dsl_domain_generate_and_consume() {
    let _ = env_logger::init();

    let mut domain = Domain::default();

    let e_count = domain.entities.len();

    domain
        .interpret_dsl(&"add entity abc; remove entity abc; add entity post;")
        .ok();

    let script = domain.to_text_dsl();

    let mut next_domain = Domain::default();
    next_domain.interpret_dsl(&script).ok();

    assert_eq!(domain, next_domain);
}

#[test]
fn test_dsl_model_interpret() {
    let _ = env_logger::init();

    let mut model = Model::default();
    assert_eq!(model.domain.doc.entities.len(), 0);

    let _ = model.interpret_dsl(&"with domain { add entity zork; };");
    assert_eq!(model.domain.doc.entities.len(), 1);

    let _ = model.interpret_dsl(&"with domain { remove entity zork; };");
    assert_eq!(model.domain.doc.entities.len(), 0);

}

#[test]
fn test_dsl_model_interpret_multiline() {
    let _ = env_logger::init();

    let mut model = Model::default();
    assert_eq!(model.domain.doc.entities.len(), 0);

    let _ = model.interpret_dsl(
        &r#"with domain { add entity zork; add entity bork; add entity fnord; };"#,
    );
    assert_eq!(model.domain.doc.entities.len(), 3);

    let _ = model.interpret_dsl(
        &r#"with domain { remove entity zork; remove entity bork; };"#,
    );
    assert_eq!(model.domain.doc.entities.len(), 1);

}
<|endoftext|>


<commit_before>//
// An experimental Baml parser in Rust
//
// Copyright (c) 2014 by Michael Neumann (mneumann@ntecs.de)
//

use std::str::from_utf8;
use std::str::{MaybeOwned,Slice,Owned};
use std::result::{Result, Ok, Err};

static baml: &'static [u8] = bytes!(r###"
html {
  head {
    title "Hello World"
  }
  body {
    h1 "Hello World"
  }
}
"###);

//fn skip_whitespace<'a>(str: &'a str) -> &'a str {
//  str.trim_left_chars(& &[' ', '\t', '\r', '\n'])
//}

/*fn skip_comment<'a>(str: &'a str) -> &'a str {
  if str.starts_with("//") {
    // str.trim_left_chars(&|c: char|
    // consume whole line
    str.find('\n')
  }
}
*/

/*
fn parse_element(str: &str) {
    // let mut str = skip_whitespace(str);
    let mut iter = str.chars2();

    loop {
        match iter.next() {
            Some(c) => {
                match c {
                    ' ' | '\n' => {}
                    'a' .. 'z' => {
                    }
                }
            }
            None => break
        }
    }
}
*/

#[deriving(Show,Eq)]
enum Document<'a> {
  //SingleTag(MaybeOwned<'a>)
  SingleTag(&'a str)
}

fn is_alpha(ch: u8) -> bool {
  if ch >= ('a' as u8) && ch <= ('z' as u8) { return true }
  if ch >= ('A' as u8) && ch <= ('Z' as u8) { return true }
  return false;
}

fn is_alnum(ch: u8) -> bool {
  if ch >= ('0' as u8) && ch <= ('9' as u8) { return true }
  if is_alpha(ch) { return true }
  return false;
}

struct SliceReader<'a> {
  data: &'a[u8]
}

impl<'a> SliceReader<'a> {

  fn new(data: &'a[u8]) -> SliceReader<'a> {
      SliceReader { data: data }
  }

  fn len(&self) -> uint { self.data.len() }
  fn is_empty(&self) -> bool { self.len() == 0 }
  fn data(&self) -> &'a[u8] { self.data }

  fn head(&mut self) -> Option<u8> {
    self.data.head().map(|c| *c)
  }

  /// Remove `n` (but no more than len()) items from the back and return them.
  fn pop_back(&mut self, n: uint) -> &'a[u8] {
    if n > self.len() { debug!("pop_back(): n > len"); }
    let n = std::cmp::min(n, self.len());
    let (front, back) = self.split_at(self.len() - n);
    self.data = front;
    return back;
  }

  /// Remove `n` (but no more than len()) items from the front and return them.
  fn pop_front(&mut self, n: uint) -> &'a[u8] {
    if n > self.len() { debug!("pop_front(): n > len"); }
    let n = std::cmp::min(n, self.len());

    let (front, back) = self.split_at(n);
    self.data = back;
    return front;
  }

  fn pop_front_while(&mut self, cond: |u8| -> bool) -> &'a[u8] {
    let mut cnt = 0;
    for &b in self.data.iter() {
      if cond(b) {
        cnt += 1;
      } else {
        break;
      }
    }
    self.pop_front(cnt)
  }

  fn split_at(&self, pos: uint) -> (&'a[u8], &'a[u8]) {
    assert!(pos <= self.data.len());
    (self.data.slice(0, pos), self.data.slice(pos, self.data.len()))
  }
}

/*
fn parse_element<'a>(buf: &'a [u8]) -> Result<Document<'a>, ()> {
  let ch = buf[0];

  if is_alpha(ch) {
    let beg = 0;
    let mut end = beg+1;
    while end < buf.len() {
      if is_alnum(buf[end]) { end += 1; }
      else { break }
    }
    Ok(SingleTag(from_utf8(buf.slice(beg, end)).unwrap()))
  }
  else {
    fail!()
  }
}
*/

fn parse_element<'a>(rd: &'a mut SliceReader) -> Result<Document<'a>, ()> {
  let ch = rd.head();
  if ch.is_none() { return Err(()); }

  if is_alpha(ch.unwrap()) {
    let tag = rd.pop_front_while(|c| is_alnum(c));
    Ok(SingleTag(from_utf8(tag).unwrap()))
  }
  else {
    fail!()
  }
}

#[test]
fn test_single_tag() {
  let str = bytes!("html");
  let mut rd = SliceReader::new(str);

  assert_eq!(parse_element(&mut rd), Ok(SingleTag("html")));
}
<commit_msg>Cleanup and ignore whitespace<commit_after>//
// An experimental Baml parser in Rust
//
// Copyright (c) 2014 by Michael Neumann (mneumann@ntecs.de)
//

use std::str::from_utf8;
use std::str::{MaybeOwned,Slice,Owned};
use std::result::{Result, Ok, Err};

static baml: &'static [u8] = bytes!(r###"
html {
  head {
    title "Hello World"
  }
  body {
    h1 "Hello World"
  }
}
"###);

#[deriving(Show,Eq)]
enum Document<'a> {
  //SingleTag(MaybeOwned<'a>)
  SingleTag(&'a str)
}

fn is_alpha(ch: u8) -> bool {
  if ch >= ('a' as u8) && ch <= ('z' as u8) { return true }
  if ch >= ('A' as u8) && ch <= ('Z' as u8) { return true }
  return false;
}

fn is_alnum(ch: u8) -> bool {
  if ch >= ('0' as u8) && ch <= ('9' as u8) { return true }
  if is_alpha(ch) { return true }
  return false;
}

/*
fn is_space(ch: u8) -> bool {
  if ch == (' ' as u8) || ch == ('\t' as u8) || ch == (
  if ch >= ('A' as u8) && ch <= ('Z' as u8) { return true }
  return false;
}
*/

struct SliceReader<'a> {
  data: &'a[u8]
}

impl<'a> SliceReader<'a> {

  fn new(data: &'a[u8]) -> SliceReader<'a> {
      SliceReader { data: data }
  }

  fn len(&self) -> uint { self.data.len() }
  fn is_empty(&self) -> bool { self.len() == 0 }
  fn data(&self) -> &'a[u8] { self.data }

  fn head(&mut self) -> Option<u8> {
    self.data.head().map(|c| *c)
  }

  /// Remove `n` (but no more than len()) items from the back and return them.
  fn pop_back(&mut self, n: uint) -> &'a[u8] {
    if n > self.len() { debug!("pop_back(): n > len"); }
    let n = std::cmp::min(n, self.len());
    let (front, back) = self.split_at(self.len() - n);
    self.data = front;
    return back;
  }

  /// Remove `n` (but no more than len()) items from the front and return them.
  fn pop_front(&mut self, n: uint) -> &'a[u8] {
    if n > self.len() { debug!("pop_front(): n > len"); }
    let n = std::cmp::min(n, self.len());

    let (front, back) = self.split_at(n);
    self.data = back;
    return front;
  }

  fn pop_front_while(&mut self, cond: |u8| -> bool) -> &'a[u8] {
    let mut cnt = 0;
    for &b in self.data.iter() {
      if cond(b) {
        cnt += 1;
      } else {
        break;
      }
    }
    self.pop_front(cnt)
  }

  fn split_at(&self, pos: uint) -> (&'a[u8], &'a[u8]) {
    assert!(pos <= self.data.len());
    (self.data.slice(0, pos), self.data.slice(pos, self.data.len()))
  }
}

// skips whitespace including newlines
fn skip_whitespace<'a>(rd: &'a mut SliceReader) {
  let _ = rd.pop_front_while(|c|
    c == (' ' as u8) || c == ('\t' as u8) || c == ('\r' as u8) || c == ('\n' as u8)
  );
}

fn parse_element<'a>(rd: &'a mut SliceReader) -> Result<Document<'a>, ()> {
  skip_whitespace(rd);

  let ch = rd.head();
  if ch.is_none() { return Err(()); }

  if is_alpha(ch.unwrap()) {
    let tag = rd.pop_front_while(|c| is_alnum(c));
    Ok(SingleTag(from_utf8(tag).unwrap()))
  }
  else {
    fail!()
  }
}

#[test]
fn test_single_tag() {
  let str = bytes!(" html");
  let mut rd = SliceReader::new(str);

  assert_eq!(parse_element(&mut rd), Ok(SingleTag("html")));
}
<|endoftext|>


<commit_before>use std;

pub type id = libc::intptr_t;
pub type Class = libc::intptr_t;
pub type IMP = *u8;
pub type SEL = libc::intptr_t;
pub type Ivar = libc::intptr_t;

pub const nil : id = 0 as id;

pub extern mod objc {
    fn class_addMethod(cls : Class,
                       name : SEL,
                       imp : IMP,
                       types : *libc::c_char) -> bool;
    fn class_addIvar(cls : Class,
                     name : *libc::c_char,
                     size : libc::size_t,
                     alignment: u8,
		             types: *libc::c_char) -> bool;
    fn object_setInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  value : *libc::c_void);
    fn object_getInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  outValue : **libc::c_void);
    fn objc_allocateClassPair(superclass : Class,
                              name : *libc::c_char,
                              extraBytes : libc::size_t) -> Class;
    fn objc_getClass(name : *libc::c_char) -> id;
    fn objc_msgSend(theReceiver : id, theSelector : SEL) -> id;
    fn objc_registerClassPair(cls : Class);
    fn sel_registerName(name : *libc::c_char) -> SEL;
}

#[test]
pub fn test_nsapp() {
    let klass = str::as_c_str(~"NSApplication", |s|
        objc::objc_getClass(s)
    );

    let sel = str::as_c_str(~"sharedApplication", |s|
        objc::sel_registerName(s)
    );

    let nsapp = objc::objc_msgSend(klass, sel);

    io::println(#fmt("nsapp: %d", (nsapp as int)));
}

#[test]
pub fn test_custom_obj() {
    extern fn MyObject_doSomething(this : id, _sel : SEL) -> id {
        io::println(~"doSomething");
        return this;
    }

    let NSObject = str::as_c_str(~"NSObject", |s|
        objc::objc_getClass(s)
    );
    let MyObject = str::as_c_str(~"MyObject", |s|
        objc::objc_allocateClassPair(NSObject, s, 0 as libc::size_t)
    );
    let doSomething = str::as_c_str(~"doSomething", |s|
        objc::sel_registerName(s)
    );
    let _ = str::as_c_str(~"@@:", |types|
        objc::class_addMethod(MyObject, doSomething, MyObject_doSomething,
                              types)
    );
    objc::objc_registerClassPair(MyObject);

    let alloc = str::as_c_str(~"alloc", |s| objc::sel_registerName(s));
    let init = str::as_c_str(~"init", |s| objc::sel_registerName(s));

    let mut obj = objc::objc_msgSend(MyObject, alloc);
    obj = objc::objc_msgSend(obj, init);
    objc::objc_msgSend(obj, doSomething);
}

<commit_msg>Update for language changes<commit_after>use std;

pub type id = libc::intptr_t;
pub type Class = libc::intptr_t;
pub type IMP = *u8;
pub type SEL = libc::intptr_t;
pub type Ivar = libc::intptr_t;

pub const nil : id = 0 as id;

pub extern mod objc {
    fn class_addMethod(cls : Class,
                       name : SEL,
                       imp : IMP,
                       types : *libc::c_char) -> bool;
    fn class_addIvar(cls : Class,
                     name : *libc::c_char,
                     size : libc::size_t,
                     alignment: u8,
		             types: *libc::c_char) -> bool;
    fn object_setInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  value : *libc::c_void);
    fn object_getInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  outValue : **libc::c_void);
    fn objc_allocateClassPair(superclass : Class,
                              name : *libc::c_char,
                              extraBytes : libc::size_t) -> Class;
    fn objc_getClass(name : *libc::c_char) -> id;
    fn objc_msgSend(theReceiver : id, theSelector : SEL) -> id;
    fn objc_registerClassPair(cls : Class);
    fn sel_registerName(name : *libc::c_char) -> SEL;
}

#[test]
pub fn test_nsapp() {
    let klass = str::as_c_str(~"NSApplication", |s|
        objc::objc_getClass(s)
    );

    let sel = str::as_c_str(~"sharedApplication", |s|
        objc::sel_registerName(s)
    );

    let nsapp = objc::objc_msgSend(klass, sel);

    io::println(fmt!("nsapp: %d", (nsapp as int)));
}

#[test]
pub fn test_custom_obj() {
    extern fn MyObject_doSomething(this : id, _sel : SEL) -> id {
        io::println(~"doSomething");
        return this;
    }

    let NSObject = str::as_c_str(~"NSObject", |s|
        objc::objc_getClass(s)
    );
    let MyObject = str::as_c_str(~"MyObject", |s|
        objc::objc_allocateClassPair(NSObject, s, 0 as libc::size_t)
    );
    let doSomething = str::as_c_str(~"doSomething", |s|
        objc::sel_registerName(s)
    );
    let _ = str::as_c_str(~"@@:", |types|
        objc::class_addMethod(MyObject, doSomething, MyObject_doSomething,
                              types)
    );
    objc::objc_registerClassPair(MyObject);

    let alloc = str::as_c_str(~"alloc", |s| objc::sel_registerName(s));
    let init = str::as_c_str(~"init", |s| objc::sel_registerName(s));

    let mut obj = objc::objc_msgSend(MyObject, alloc);
    obj = objc::objc_msgSend(obj, init);
    objc::objc_msgSend(obj, doSomething);
}

<|endoftext|>