<commit_before>extern crate clap;
#[macro_use]
extern crate glium;
extern crate rand;

use std::collections::{HashSet, VecDeque};

use glium::Surface;
use glium::glutin;

/// Maximum frames per second.
const FPS: u32 = 30;

#[derive(Clone, Copy, Debug, PartialEq)]
enum Color {
    Red,
    Yellow,
    Green,
    LightBrown,
    Purple,
    Cyan,
    Blue,
    Fuchsia,
}

const COLORS: &[Color] = &[
    Color::Red,
    Color::Yellow,
    Color::Green,
    Color::LightBrown,
    Color::Purple,
    Color::Cyan,
    Color::Blue,
    Color::Fuchsia,
];

impl rand::Rand for Color {
    fn rand<R: rand::Rng>(rng: &mut R) -> Self {
        *rng.choose(COLORS).unwrap()
    }
}

struct Grid {
    width: u8,
    height: u8,
    num_colors: u8,

    cells: Vec<Color>,
    population: Vec<u16>,

    max_clicks: u16,
    num_clicks: u16,
}

impl Grid {
    pub fn new(num_colors: u8, size: u8) -> Self {
        use rand::distributions::{IndependentSample, Range};
        let between = Range::new(0, num_colors as usize);
        let mut rng = rand::thread_rng();

        let cells: Vec<Color> = (0..size as u64 * size as u64)
            .map(|_| COLORS[between.ind_sample(&mut rng)])
            .collect();
        let max_clicks = 25 * 2 * size as u16 * num_colors as u16 / (2 * 14 * 6);
        let mut population = vec![0; COLORS.len()];
        for &c in &cells {
            population[c as usize] += 1;
        }

        Self {
            width: size,
            height: size,
            num_colors,

            cells,
            population,

            max_clicks,
            num_clicks: 0,
        }
    }

    fn index(&self, row: u8, column: u8) -> usize {
        self.width as usize * row as usize + column as usize
    }

    fn current_color(&self) -> Color {
        self.cells[0]
    }

    fn solved(&self) -> bool {
        let mut colors_present = 0;
        for &x in &self.population {
            if x > 0 {
                colors_present += 1;
            }
        }
        colors_present == 1
    }

    pub fn click(&mut self, row: u8, column: u8) -> bool {
        let i = self.index(row, column);
        let new_color = self.cells[i];
        if self.current_color() == new_color {
            return false;
        }

        self.num_clicks += 1;
        self.flood(new_color);
        self.solved()
    }

    /// Flood the grid from the top left cell and return the number of cell which are connected to
    /// the top left cell by cells of indentical color.
    fn flood(&mut self, new_color: Color) {
        let current_color = self.current_color();

        let rows = self.height as usize;
        let columns = self.width as usize;

        let mut visited = HashSet::new();
        let mut queue = VecDeque::new();
        queue.push_back(0);

        while let Some(i) = queue.pop_front() {
            if self.cells[i] == new_color {
                continue;
            }
            let c = self.cells[i];
            self.population[c as usize] -= 1;
            self.cells[i] = new_color;
            self.population[new_color as usize] += 1;
            visited.insert(i);

            if i % columns > 0 && self.cells[i - 1] == current_color &&
                !visited.contains(&(i - 1))
            {
                queue.push_back(i - 1);
            }
            if i % columns < columns - 1 && self.cells[i + 1] == current_color &&
                !visited.contains(&(i + 1))
            {
                queue.push_back(i + 1);
            }
            if i >= columns && self.cells[i - columns] == current_color &&
                !visited.contains(&(i - columns))
            {
                queue.push_back(i - columns);
            }
            if i < (rows - 1) * columns && self.cells[i + columns] == current_color &&
                !visited.contains(&(i + columns))
            {
                queue.push_back(i + columns);
            }
        }
    }

    pub fn aspect_ratio(&self) -> f32 {
        self.height as f32 / self.width as f32
    }

    pub fn reset(&mut self) {
        use rand::distributions::{IndependentSample, Range};
        let between = Range::new(0, self.num_colors as usize);
        let mut rng = rand::thread_rng();

        self.cells.iter_mut().for_each(|x| {
            *x = COLORS[between.ind_sample(&mut rng)]
        });

        self.population = vec![0; COLORS.len()];
        for &c in &self.cells {
            self.population[c as usize] += 1;
        }
        self.num_clicks = 0;
    }
}

#[derive(Copy, Clone)]
struct Vertex {
    color: f32,
    position: [f32; 2],
}

implement_vertex!(Vertex, color, position);

const TITLE: &'static str = "Flood-It";

const VERTEX_SHADER: &str = r#"
    #version 140
    in vec2 position;
    in float color;
    uniform mat4 matrix;
    out float c;
    void main() {
        c = color;
        gl_Position = matrix * vec4(position, 0.0, 1.0);
    }
"#;

/// Map Color::* to RGB values.
const FRAGMENT_SHADER: &str = r#"
    #version 140
    in float c;
    out vec4 color;
    void main() {
        if (c <= 0.0)
            color = vec4(1.0, 0.0, 0.0, 1.0);
        else if (c <= 1.0)
            color = vec4(1.0, 1.0, 0.0, 1.0);
        else if (c <= 2.0)
            color = vec4(0.0, 0.69, 0.0, 1.0);
        else if (c <= 3.0)
            color = vec4(1.0, 0.8, 0.4, 1.0);
        else if (c <= 4.0)
            color = vec4(0.5, 0.0, 0.5, 1.0);
        else if (c <= 5.0)
            color = vec4(0.0, 1.0, 1.0, 1.0);
        else if (c <= 6.0)
            color = vec4(0.0, 0.0, 1.0, 1.0);
        else if (c <= 7.0)
            color = vec4(1.0, 0.0, 1.0, 1.0);
        else
            // Should never happen
            color = vec4(1.0, 1.0, 1.0, 1.0);
    }
"#;

fn main() {
    use clap::{App, Arg};
    use glium::glutin::{ContextBuilder, ElementState, EventsLoop, KeyboardInput, WindowBuilder};
    use glutin::{Event, WindowEvent};

    let matches = App::new(TITLE)
        .author(env!("CARGO_PKG_AUTHORS"))
        .version(env!("CARGO_PKG_VERSION"))
        .arg(
            Arg::with_name("colors")
                .takes_value(true)
                .help("The number of different colors")
                .default_value("6"),
        )
        .arg(
            Arg::with_name("size")
                .takes_value(true)
                .help("The height and width of the grid")
                .default_value("14"),
        )
        .get_matches();

    let colors = {
        let tmp: u8 = matches
            .value_of("colors")
            .and_then(|x| x.parse().ok())
            .unwrap_or(6);
        if tmp < 3 || tmp > 8 {
            panic!("Flood-It only supports 3 through 8 (inclusive) colors.");
        } else {
            tmp
        }
    };
    let size: u8 = {
        let tmp: u8 = matches
            .value_of("size")
            .and_then(|x| x.parse().ok())
            .unwrap_or(14);
        if tmp < 2 {
            panic!("Flood-It needs a grid of at least 2x2 cells.");
        } else {
            tmp
        }
    };

    let mut events_loop = EventsLoop::new();
    let window = WindowBuilder::new().with_title(TITLE);
    let context = ContextBuilder::new();
    let display = glium::Display::new(window, context, &events_loop).unwrap();

    let program = glium::Program::from_source(&display, VERTEX_SHADER, FRAGMENT_SHADER, None)
        .unwrap();

    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    let params = glium::DrawParameters {
        backface_culling: glium::draw_parameters::BackfaceCullingMode::CullCounterClockwise,
        ..Default::default()
    };

    let mut grid = Grid::new(colors, size);
    let grid_aspect_ratio = grid.aspect_ratio();

    let mut cursor_position = (0.0, 0.0);

    main_loop(|| {
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 1.0);
        let (width, height) = target.get_dimensions();
        let screen_aspect_ratio = height as f32 / width as f32;

        let (ratio_x, ratio_y) = if screen_aspect_ratio < grid_aspect_ratio {
            (screen_aspect_ratio, 1.0)
        } else {
            (1.0, screen_aspect_ratio.recip())
        };

        let uniforms =
            uniform! {
            matrix: [
                [ratio_x, 0.0, 0.0, 0.0],
                [0.0, -ratio_y, 0.0, 0.0],
                [0.0, 0.0, 1.0, 0.0],
                [0.0, 0.0, 0.0, 1.0_f32],
            ]
        };

        let mut triangles = vec![];

        for i in 0..grid.cells.len() {
            let x = (i % grid.width as usize) as f32;
            let y = (i / grid.width as usize) as f32;

            let cell_size = 2.0 / grid.height.max(grid.width) as f32;

            let mut quad = generate_rectangle_vertices(
                x * cell_size - 1.0,
                y * cell_size - 1.0,
                (x + 1.0) * cell_size - 1.0,
                (y + 1.0) * cell_size - 1.0,
                grid.cells[i],
            );
            triangles.append(&mut quad);
        }

        let vertex_buffer = glium::VertexBuffer::new(&display, &triangles).unwrap();
        target
            .draw(&vertex_buffer, &indices, &program, &uniforms, &params)
            .unwrap();

        target.finish().unwrap();

        let mut closed = false;
        events_loop.poll_events(|event| match event {
            Event::WindowEvent { event, .. } => {
                match event {
                    WindowEvent::Closed => closed = true,
                    WindowEvent::MouseMoved { position, .. } =>
                        cursor_position = position,
                    WindowEvent::KeyboardInput { input: KeyboardInput{virtual_keycode: Some(key), state: ElementState::Released, ..}, ..} => {
                        use glium::glutin::VirtualKeyCode;
                        match key {
                            VirtualKeyCode::Space | VirtualKeyCode::N | VirtualKeyCode::R =>
                                if grid.solved() {
                                    grid.reset();
                                }
                            _ => (),
                        }
                    }
                    WindowEvent::MouseInput {
                        state: glium::glutin::ElementState::Released,
                        button: glium::glutin::MouseButton::Left,
                        ..
                    } => {
                        let offsets = {
                            let offset = (width as f64 - height as f64).abs() / 2.0;
                            if screen_aspect_ratio < grid_aspect_ratio {
                                (offset, 0.0)
                            } else {
                                (0.0, offset)
                            }
                        };
                        if cursor_position.0 - offsets.0 >= 0.0 &&
                            cursor_position.1 - offsets.1 >= 0.0
                        {
                            let column = ((cursor_position.0 - offsets.0) * grid.width as f64 /
                                              (width as f64 - 2.0 * offsets.0))
                                .floor() as u8;
                            let row = ((cursor_position.1 - offsets.1) * grid.height as f64 /
                                           (height as f64 - 2.0 * offsets.1))
                                .floor() as u8;
                            if grid.click(row, column) {
                                    if grid.num_clicks <= grid.max_clicks {
                                        println!("You win! You used {} out of {} available moves.", grid.num_clicks, grid.max_clicks);
                                    } else {
                                        println!("You lose. You took {} moves but should have finished in {}.", grid.num_clicks, grid.max_clicks);
                                }
                            }
                        }
                    }
                    _ => (),
                }
            }
            _ => (),
        });
        !closed
    });
}

fn generate_rectangle_vertices(
    left: f32,
    bottom: f32,
    right: f32,
    top: f32,
    color: Color,
) -> Vec<Vertex> {
    let color = color as u32 as f32;
    vec![
        Vertex {
            color,
            position: [left, bottom],
        },
        Vertex {
            color,
            position: [right, bottom],
        },
        Vertex {
            color,
            position: [left, top],
        },
        Vertex {
            color,
            position: [right, bottom],
        },
        Vertex {
            color,
            position: [right, top],
        },
        Vertex {
            color,
            position: [left, top],
        },
    ]
}

fn main_loop<F: FnMut() -> bool>(mut callback: F) {
    use std::time::{Duration, Instant};
    use std::thread;

    let one_frame = Duration::new(0, 10 ^ 9 / FPS + 1);
    let mut last_frame = Instant::now();

    while callback() {
        let now = Instant::now();
        let remaining = one_frame.checked_sub(now - last_frame).unwrap_or_default();
        last_frame = now;
        thread::sleep(remaining);
    }
}
<commit_msg>Move argument parsing to a new function<commit_after>extern crate clap;
#[macro_use]
extern crate glium;
extern crate rand;

use std::collections::{HashSet, VecDeque};

use glium::Surface;
use glium::glutin;

/// Maximum frames per second.
const FPS: u32 = 30;

#[derive(Clone, Copy, Debug, PartialEq)]
enum Color {
    Red,
    Yellow,
    Green,
    LightBrown,
    Purple,
    Cyan,
    Blue,
    Fuchsia,
}

const COLORS: &[Color] = &[
    Color::Red,
    Color::Yellow,
    Color::Green,
    Color::LightBrown,
    Color::Purple,
    Color::Cyan,
    Color::Blue,
    Color::Fuchsia,
];

impl rand::Rand for Color {
    fn rand<R: rand::Rng>(rng: &mut R) -> Self {
        *rng.choose(COLORS).unwrap()
    }
}

struct Grid {
    width: u8,
    height: u8,
    num_colors: u8,

    cells: Vec<Color>,
    population: Vec<u16>,

    max_clicks: u16,
    num_clicks: u16,
}

impl Grid {
    pub fn new(num_colors: u8, size: u8) -> Self {
        use rand::distributions::{IndependentSample, Range};
        let between = Range::new(0, num_colors as usize);
        let mut rng = rand::thread_rng();

        let cells: Vec<Color> = (0..size as u64 * size as u64)
            .map(|_| COLORS[between.ind_sample(&mut rng)])
            .collect();
        let max_clicks = 25 * 2 * size as u16 * num_colors as u16 / (2 * 14 * 6);
        let mut population = vec![0; COLORS.len()];
        for &c in &cells {
            population[c as usize] += 1;
        }

        Self {
            width: size,
            height: size,
            num_colors,

            cells,
            population,

            max_clicks,
            num_clicks: 0,
        }
    }

    fn index(&self, row: u8, column: u8) -> usize {
        self.width as usize * row as usize + column as usize
    }

    fn current_color(&self) -> Color {
        self.cells[0]
    }

    fn solved(&self) -> bool {
        let mut colors_present = 0;
        for &x in &self.population {
            if x > 0 {
                colors_present += 1;
            }
        }
        colors_present == 1
    }

    pub fn click(&mut self, row: u8, column: u8) -> bool {
        let i = self.index(row, column);
        let new_color = self.cells[i];
        if self.current_color() == new_color {
            return false;
        }

        self.num_clicks += 1;
        self.flood(new_color);
        self.solved()
    }

    /// Flood the grid from the top left cell and return the number of cell which are connected to
    /// the top left cell by cells of indentical color.
    fn flood(&mut self, new_color: Color) {
        let current_color = self.current_color();

        let rows = self.height as usize;
        let columns = self.width as usize;

        let mut visited = HashSet::new();
        let mut queue = VecDeque::new();
        queue.push_back(0);

        while let Some(i) = queue.pop_front() {
            if self.cells[i] == new_color {
                continue;
            }
            let c = self.cells[i];
            self.population[c as usize] -= 1;
            self.cells[i] = new_color;
            self.population[new_color as usize] += 1;
            visited.insert(i);

            if i % columns > 0 && self.cells[i - 1] == current_color &&
                !visited.contains(&(i - 1))
            {
                queue.push_back(i - 1);
            }
            if i % columns < columns - 1 && self.cells[i + 1] == current_color &&
                !visited.contains(&(i + 1))
            {
                queue.push_back(i + 1);
            }
            if i >= columns && self.cells[i - columns] == current_color &&
                !visited.contains(&(i - columns))
            {
                queue.push_back(i - columns);
            }
            if i < (rows - 1) * columns && self.cells[i + columns] == current_color &&
                !visited.contains(&(i + columns))
            {
                queue.push_back(i + columns);
            }
        }
    }

    pub fn aspect_ratio(&self) -> f32 {
        self.height as f32 / self.width as f32
    }

    pub fn reset(&mut self) {
        use rand::distributions::{IndependentSample, Range};
        let between = Range::new(0, self.num_colors as usize);
        let mut rng = rand::thread_rng();

        self.cells.iter_mut().for_each(|x| {
            *x = COLORS[between.ind_sample(&mut rng)]
        });

        self.population = vec![0; COLORS.len()];
        for &c in &self.cells {
            self.population[c as usize] += 1;
        }
        self.num_clicks = 0;
    }
}

#[derive(Copy, Clone)]
struct Vertex {
    color: f32,
    position: [f32; 2],
}

implement_vertex!(Vertex, color, position);

const TITLE: &'static str = "Flood-It";

const VERTEX_SHADER: &str = r#"
    #version 140
    in vec2 position;
    in float color;
    uniform mat4 matrix;
    out float c;
    void main() {
        c = color;
        gl_Position = matrix * vec4(position, 0.0, 1.0);
    }
"#;

/// Map Color::* to RGB values.
const FRAGMENT_SHADER: &str = r#"
    #version 140
    in float c;
    out vec4 color;
    void main() {
        if (c <= 0.0)
            color = vec4(1.0, 0.0, 0.0, 1.0);
        else if (c <= 1.0)
            color = vec4(1.0, 1.0, 0.0, 1.0);
        else if (c <= 2.0)
            color = vec4(0.0, 0.69, 0.0, 1.0);
        else if (c <= 3.0)
            color = vec4(1.0, 0.8, 0.4, 1.0);
        else if (c <= 4.0)
            color = vec4(0.5, 0.0, 0.5, 1.0);
        else if (c <= 5.0)
            color = vec4(0.0, 1.0, 1.0, 1.0);
        else if (c <= 6.0)
            color = vec4(0.0, 0.0, 1.0, 1.0);
        else if (c <= 7.0)
            color = vec4(1.0, 0.0, 1.0, 1.0);
        else
            // Should never happen
            color = vec4(1.0, 1.0, 1.0, 1.0);
    }
"#;

fn parse_args() -> (u8, u8) {
    use clap::{App, Arg};

    let matches = App::new(TITLE)
        .author(env!("CARGO_PKG_AUTHORS"))
        .version(env!("CARGO_PKG_VERSION"))
        .arg(
            Arg::with_name("colors")
                .takes_value(true)
                .help("The number of different colors")
                .default_value("6"),
        )
        .arg(
            Arg::with_name("size")
                .takes_value(true)
                .help("The height and width of the grid")
                .default_value("14"),
        )
        .get_matches();

    let colors = {
        let tmp: u8 = matches
            .value_of("colors")
            .and_then(|x| x.parse().ok())
            .unwrap_or(6);
        if tmp < 3 || tmp > 8 {
            panic!("Flood-It only supports 3 through 8 (inclusive) colors.");
        } else {
            tmp
        }
    };
    let size: u8 = {
        let tmp: u8 = matches
            .value_of("size")
            .and_then(|x| x.parse().ok())
            .unwrap_or(14);
        if tmp < 2 {
            panic!("Flood-It needs a grid of at least 2x2 cells.");
        } else {
            tmp
        }
    };

    (colors, size)
}

fn main() {
    use glium::glutin::{ContextBuilder, ElementState, EventsLoop, KeyboardInput, WindowBuilder};
    use glutin::{Event, WindowEvent};

    let (colors, size) = parse_args();

    let mut events_loop = EventsLoop::new();
    let window = WindowBuilder::new().with_title(TITLE);
    let context = ContextBuilder::new();
    let display = glium::Display::new(window, context, &events_loop).unwrap();

    let program = glium::Program::from_source(&display, VERTEX_SHADER, FRAGMENT_SHADER, None)
        .unwrap();

    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    let params = glium::DrawParameters {
        backface_culling: glium::draw_parameters::BackfaceCullingMode::CullCounterClockwise,
        ..Default::default()
    };

    let mut grid = Grid::new(colors, size);
    let grid_aspect_ratio = grid.aspect_ratio();

    let mut cursor_position = (0.0, 0.0);

    main_loop(|| {
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 1.0);
        let (width, height) = target.get_dimensions();
        let screen_aspect_ratio = height as f32 / width as f32;

        let (ratio_x, ratio_y) = if screen_aspect_ratio < grid_aspect_ratio {
            (screen_aspect_ratio, 1.0)
        } else {
            (1.0, screen_aspect_ratio.recip())
        };

        let uniforms =
            uniform! {
            matrix: [
                [ratio_x, 0.0, 0.0, 0.0],
                [0.0, -ratio_y, 0.0, 0.0],
                [0.0, 0.0, 1.0, 0.0],
                [0.0, 0.0, 0.0, 1.0_f32],
            ]
        };

        let mut triangles = vec![];

        for i in 0..grid.cells.len() {
            let x = (i % grid.width as usize) as f32;
            let y = (i / grid.width as usize) as f32;

            let cell_size = 2.0 / grid.height.max(grid.width) as f32;

            let mut quad = generate_rectangle_vertices(
                x * cell_size - 1.0,
                y * cell_size - 1.0,
                (x + 1.0) * cell_size - 1.0,
                (y + 1.0) * cell_size - 1.0,
                grid.cells[i],
            );
            triangles.append(&mut quad);
        }

        let vertex_buffer = glium::VertexBuffer::new(&display, &triangles).unwrap();
        target
            .draw(&vertex_buffer, &indices, &program, &uniforms, &params)
            .unwrap();

        target.finish().unwrap();

        let mut closed = false;
        events_loop.poll_events(|event| match event {
            Event::WindowEvent { event, .. } => {
                match event {
                    WindowEvent::Closed => closed = true,
                    WindowEvent::MouseMoved { position, .. } =>
                        cursor_position = position,
                    WindowEvent::KeyboardInput { input: KeyboardInput{virtual_keycode: Some(key), state: ElementState::Released, ..}, ..} => {
                        use glium::glutin::VirtualKeyCode;
                        match key {
                            VirtualKeyCode::Space | VirtualKeyCode::N | VirtualKeyCode::R =>
                                if grid.solved() {
                                    grid.reset();
                                }
                            _ => (),
                        }
                    }
                    WindowEvent::MouseInput {
                        state: glium::glutin::ElementState::Released,
                        button: glium::glutin::MouseButton::Left,
                        ..
                    } => {
                        let offsets = {
                            let offset = (width as f64 - height as f64).abs() / 2.0;
                            if screen_aspect_ratio < grid_aspect_ratio {
                                (offset, 0.0)
                            } else {
                                (0.0, offset)
                            }
                        };
                        if cursor_position.0 - offsets.0 >= 0.0 &&
                            cursor_position.1 - offsets.1 >= 0.0
                        {
                            let column = ((cursor_position.0 - offsets.0) * grid.width as f64 /
                                              (width as f64 - 2.0 * offsets.0))
                                .floor() as u8;
                            let row = ((cursor_position.1 - offsets.1) * grid.height as f64 /
                                           (height as f64 - 2.0 * offsets.1))
                                .floor() as u8;
                            if grid.click(row, column) {
                                    if grid.num_clicks <= grid.max_clicks {
                                        println!("You win! You used {} out of {} available moves.", grid.num_clicks, grid.max_clicks);
                                    } else {
                                        println!("You lose. You took {} moves but should have finished in {}.", grid.num_clicks, grid.max_clicks);
                                }
                            }
                        }
                    }
                    _ => (),
                }
            }
            _ => (),
        });
        !closed
    });
}

fn generate_rectangle_vertices(
    left: f32,
    bottom: f32,
    right: f32,
    top: f32,
    color: Color,
) -> Vec<Vertex> {
    let color = color as u32 as f32;
    vec![
        Vertex {
            color,
            position: [left, bottom],
        },
        Vertex {
            color,
            position: [right, bottom],
        },
        Vertex {
            color,
            position: [left, top],
        },
        Vertex {
            color,
            position: [right, bottom],
        },
        Vertex {
            color,
            position: [right, top],
        },
        Vertex {
            color,
            position: [left, top],
        },
    ]
}

fn main_loop<F: FnMut() -> bool>(mut callback: F) {
    use std::time::{Duration, Instant};
    use std::thread;

    let one_frame = Duration::new(0, 10 ^ 9 / FPS + 1);
    let mut last_frame = Instant::now();

    while callback() {
        let now = Instant::now();
        let remaining = one_frame.checked_sub(now - last_frame).unwrap_or_default();
        last_frame = now;
        thread::sleep(remaining);
    }
}
<|endoftext|>


<commit_before>#![feature(lang_items, unboxed_closures)]

use std::io::{File, IoResult, Truncate, Write};
use std::f64::consts::PI;
use std::num::Float;
use std::num::FloatMath;
use std::rand;
use std::rand::Rng;

struct SineWave(pub f64);

impl Fn<(f64, ), f64> for SineWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SineWave(frequency) = *self;
        FloatMath::sin(t * frequency * 2.0 * PI)
    }
}

struct SquareWave(pub f64);

impl Fn<(f64, ), f64> for SquareWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SquareWave(frequency) = *self;
        let sin_wave = SineWave(frequency);
        if sin_wave(t).is_positive() { 1.0 } else { -1.0 }
    }
}

struct SawtoothWave(pub f64);

impl Fn<(f64, ), f64> for SawtoothWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SawtoothWave(frequency) = *self;
        let t_factor = t * frequency;
        t_factor - t_factor.floor()
    }
}

fn generate<F>(x: f64, f: F) -> f64 where F: Fn<(f64, ), f64> {
    f(x)
}

fn quantize_16(y: f64) -> i16 {
    // Quantization levels for 16 bits
    let levels = 2.0.powf(16.0) - 1.0;

    // Convert from [-1, 1] to [-2**16 / 2, 2**16 / 2]
    (y * (levels / 2.0)) as i16
}

fn make_sample_16<F>(length: f64, sample_rate: uint, waveform: F) -> Vec<i16> where F: Fn<(f64, ), f64>+Copy {
    let num_samples = (sample_rate as f64 * length).floor() as uint;
    let mut samples: Vec<i16> = Vec::with_capacity(num_samples);

    for i in range(0u, num_samples) {
        let t = i as f64 / sample_rate as f64;
        samples.push(quantize_16(generate(t, waveform)));
    }

    samples
}

fn write_pcm(filename: &str, samples: Vec<i16>) -> IoResult<()> {
    let path = Path::new(filename);
    let mut f = match File::open_mode(&path, Truncate, Write) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    for sample in samples.iter() {
        try!(f.write_le_i16(*sample));
    }

    Ok(())
}

// See: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
fn write_wav(filename: &str, sample_rate: uint, samples: Vec<i16>) -> IoResult<()> {
    let path = Path::new(filename);
    let mut f = match File::open_mode(&path, Truncate, Write) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    // Some WAV header fields
    let channels = 1;
    let bit_depth = 16;
    let subchunk_2_size = samples.len() * channels * bit_depth / 8;
    let chunk_size = 36 + subchunk_2_size as i32;
    let byte_rate = (sample_rate * channels * bit_depth / 8) as i32;
    let block_align = (channels * bit_depth / 8) as i16;

    try!(f.write_be_i32(0x52494646))              // ChunkID, RIFF
    try!(f.write_le_i32(chunk_size));             // ChunkSize
    try!(f.write_be_i32(0x57415645));             // Format, WAVE

    try!(f.write_be_i32(0x666d7420));             // Subchunk1ID, fmt
    try!(f.write_le_i32(16));                     // Subchunk1Size, 16 for PCM
    try!(f.write_le_i16(1));                      // AudioFormat, PCM = 1 (linear quantization)
    try!(f.write_le_i16(channels as i16));        // NumChannels
    try!(f.write_le_i32(sample_rate as i32));     // SampleRate
    try!(f.write_le_i32(byte_rate));              // ByteRate
    try!(f.write_le_i16(block_align));            // BlockAlign
    try!(f.write_le_i16(bit_depth as i16));       // BitsPerSample

    try!(f.write_be_i32(0x64617461));             // Subchunk2ID, data
    try!(f.write_le_i32(subchunk_2_size as i32)); // Subchunk2Size, number of bytes in the data

    for sample in samples.iter() {
        try!(f.write_le_i16(*sample))
    }

    Ok(())
}

fn main() {
    println!("Hello, synthrs!");

    write_pcm("out/sin.pcm", make_sample_16(1.0, 44100, SineWave(440.0))).ok();
    write_wav("out/sin.wav", 44100, make_sample_16(1.0, 44100, SineWave(440.0))).ok();
    write_wav("out/square.wav", 44100, make_sample_16(1.0, 44100, SquareWave(440.0))).ok();
    write_wav("out/sawtooth.wav", 44100, make_sample_16(1.0, 44100, SawtoothWave(440.0))).ok();

    write_wav("out/wolftone.wav", 44100, make_sample_16(1.0, 44100, |t: f64| -> f64 {
        (SquareWave(1000.0)(t) + SquareWave(1020.0)(t)) / 2.0
    })).ok();

    write_wav("out/whitenoise.wav", 44100, make_sample_16(1.0, 44100, |_t: f64| -> f64 {
        let mut rng = rand::task_rng();
        (rng.gen::<f64>() - 0.5) * 2.0
    })).ok();

    write_wav("out/rising.wav", 44100, make_sample_16(1.0, 44100, |t: f64| -> f64 {
        let (min_f, max_f) = (1000.0, 8000.0);
        let max_t = 1.0; // Duration of clip in seconds
        let range = max_f - min_f;
        let f = max_f - (max_t - t) * range;
        SineWave(f)(t)
    })).ok();

    write_wav("out/racecar.wav", 44100, make_sample_16(15.0, 44100, |t: f64| -> f64 {
        let mut rng = rand::task_rng();
        let mut out = 0.0;
        if t < 14.0 { out += SawtoothWave(20.315 * (t / 2.0))(t); } // Engine
        if t < 1.0 { out += SawtoothWave(15.0)(t) * 10.0; } // Engine start
        if t < 14.0 && t.tan() > 0.0 { out += SquareWave(t.fract() * 261.625)(t); } // Gear
        if t < 14.0 && t.tan() < 0.0 { out += SquareWave(t.sin() * 523.25)(t); } // Gear
        if t < 14.0 && t.tan() > 0.866 { out += SquareWave(t.fract() * 1046.5)(t); } // Gear
        if t > 14.0 { out += (rng.gen::<f64>() - 0.5) * 8.0 } // Tree
        (out / 4.0).min(1.0)
    })).ok();

    write_wav("out/shepard.wav", 44100, make_sample_16(30.0, 44100, |t: f64| -> f64 {
        let length = 10.0;
        let t_mod = t % length;
        let progress = t_mod / length;
        let tone_a2 = SineWave(110.0 / (1.0 + progress));
        let tone_a3 = SineWave(220.0 / (1.0 + progress));
        let tone_a4 = SineWave(440.0 / (1.0 + progress));
        let tone_a5 = SineWave(880.0 / (1.0 + progress));
        (tone_a2(t_mod) * (1.0 - progress)
        + tone_a5(t_mod) * progress
        + tone_a3(t_mod) + tone_a4(t_mod)) / 4.0
    })).ok();
}
<commit_msg>Update racecar example for frequency fix<commit_after>#![feature(lang_items, unboxed_closures)]

use std::io::{File, IoResult, Truncate, Write};
use std::f64::consts::PI;
use std::num::Float;
use std::num::FloatMath;
use std::rand;
use std::rand::Rng;

struct SineWave(pub f64);

impl Fn<(f64, ), f64> for SineWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SineWave(frequency) = *self;
        FloatMath::sin(t * frequency * 2.0 * PI)
    }
}

struct SquareWave(pub f64);

impl Fn<(f64, ), f64> for SquareWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SquareWave(frequency) = *self;
        let sin_wave = SineWave(frequency);
        if sin_wave(t).is_positive() { 1.0 } else { -1.0 }
    }
}

struct SawtoothWave(pub f64);

impl Fn<(f64, ), f64> for SawtoothWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SawtoothWave(frequency) = *self;
        let t_factor = t * frequency;
        t_factor - t_factor.floor()
    }
}

fn generate<F>(x: f64, f: F) -> f64 where F: Fn<(f64, ), f64> {
    f(x)
}

fn quantize_16(y: f64) -> i16 {
    // Quantization levels for 16 bits
    let levels = 2.0.powf(16.0) - 1.0;

    // Convert from [-1, 1] to [-2**16 / 2, 2**16 / 2]
    (y * (levels / 2.0)) as i16
}

fn make_sample_16<F>(length: f64, sample_rate: uint, waveform: F) -> Vec<i16> where F: Fn<(f64, ), f64>+Copy {
    let num_samples = (sample_rate as f64 * length).floor() as uint;
    let mut samples: Vec<i16> = Vec::with_capacity(num_samples);

    for i in range(0u, num_samples) {
        let t = i as f64 / sample_rate as f64;
        samples.push(quantize_16(generate(t, waveform)));
    }

    samples
}

fn write_pcm(filename: &str, samples: Vec<i16>) -> IoResult<()> {
    let path = Path::new(filename);
    let mut f = match File::open_mode(&path, Truncate, Write) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    for sample in samples.iter() {
        try!(f.write_le_i16(*sample));
    }

    Ok(())
}

// See: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
fn write_wav(filename: &str, sample_rate: uint, samples: Vec<i16>) -> IoResult<()> {
    let path = Path::new(filename);
    let mut f = match File::open_mode(&path, Truncate, Write) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    // Some WAV header fields
    let channels = 1;
    let bit_depth = 16;
    let subchunk_2_size = samples.len() * channels * bit_depth / 8;
    let chunk_size = 36 + subchunk_2_size as i32;
    let byte_rate = (sample_rate * channels * bit_depth / 8) as i32;
    let block_align = (channels * bit_depth / 8) as i16;

    try!(f.write_be_i32(0x52494646))              // ChunkID, RIFF
    try!(f.write_le_i32(chunk_size));             // ChunkSize
    try!(f.write_be_i32(0x57415645));             // Format, WAVE

    try!(f.write_be_i32(0x666d7420));             // Subchunk1ID, fmt
    try!(f.write_le_i32(16));                     // Subchunk1Size, 16 for PCM
    try!(f.write_le_i16(1));                      // AudioFormat, PCM = 1 (linear quantization)
    try!(f.write_le_i16(channels as i16));        // NumChannels
    try!(f.write_le_i32(sample_rate as i32));     // SampleRate
    try!(f.write_le_i32(byte_rate));              // ByteRate
    try!(f.write_le_i16(block_align));            // BlockAlign
    try!(f.write_le_i16(bit_depth as i16));       // BitsPerSample

    try!(f.write_be_i32(0x64617461));             // Subchunk2ID, data
    try!(f.write_le_i32(subchunk_2_size as i32)); // Subchunk2Size, number of bytes in the data

    for sample in samples.iter() {
        try!(f.write_le_i16(*sample))
    }

    Ok(())
}

fn main() {
    println!("Hello, synthrs!");

    write_pcm("out/sin.pcm", make_sample_16(1.0, 44100, SineWave(440.0))).ok();
    write_wav("out/sin.wav", 44100, make_sample_16(1.0, 44100, SineWave(440.0))).ok();
    write_wav("out/square.wav", 44100, make_sample_16(1.0, 44100, SquareWave(440.0))).ok();
    write_wav("out/sawtooth.wav", 44100, make_sample_16(1.0, 44100, SawtoothWave(440.0))).ok();

    write_wav("out/wolftone.wav", 44100, make_sample_16(1.0, 44100, |t: f64| -> f64 {
        (SquareWave(1000.0)(t) + SquareWave(1020.0)(t)) / 2.0
    })).ok();

    write_wav("out/whitenoise.wav", 44100, make_sample_16(1.0, 44100, |_t: f64| -> f64 {
        let mut rng = rand::task_rng();
        (rng.gen::<f64>() - 0.5) * 2.0
    })).ok();

    write_wav("out/rising.wav", 44100, make_sample_16(1.0, 44100, |t: f64| -> f64 {
        let (min_f, max_f) = (1000.0, 8000.0);
        let max_t = 1.0; // Duration of clip in seconds
        let range = max_f - min_f;
        let f = max_f - (max_t - t) * range;
        SineWave(f)(t)
    })).ok();

    write_wav("out/racecar.wav", 44100, make_sample_16(15.0, 44100, |t: f64| -> f64 {
        let mut rng = rand::task_rng();
        let mut out = 0.0;
        if t < 14.0 { out += SawtoothWave(40.63 * (t / 2.0))(t); } // Engine
        if t < 1.0 { out += SawtoothWave(30.0)(t) * 10.0; } // Engine start
        if t < 14.0 && t.tan() > 0.0 { out += SquareWave(t.fract() * 130.8125)(t); } // Gear
        if t < 14.0 && t.tan() < 0.0 { out += SquareWave(t.sin() * 261.625)(t); } // Gear
        if t < 14.0 && t.tan() > 0.866 { out += SquareWave(t.fract() * 523.25)(t); } // Gear
        if t > 14.0 { out += (rng.gen::<f64>() - 0.5) * 8.0 } // Tree
        (out / 4.0).min(1.0)
    })).ok();

    write_wav("out/shepard.wav", 44100, make_sample_16(30.0, 44100, |t: f64| -> f64 {
        let length = 10.0;
        let t_mod = t % length;
        let progress = t_mod / length;
        let tone_a2 = SineWave(110.0 / (1.0 + progress));
        let tone_a3 = SineWave(220.0 / (1.0 + progress));
        let tone_a4 = SineWave(440.0 / (1.0 + progress));
        let tone_a5 = SineWave(880.0 / (1.0 + progress));
        (tone_a2(t_mod) * (1.0 - progress)
        + tone_a5(t_mod) * progress
        + tone_a3(t_mod) + tone_a4(t_mod)) / 4.0
    })).ok();
}
<|endoftext|>


<commit_before>extern crate csv;

use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io;
use std::io::Read;
use std::io::Result;
use std::io::Write;
use std::path::MAIN_SEPARATOR;

/*
    Files are arranged and named from first 4 digits of number.

    Data is boxed in a file. The first byte of file indicates the type. Data follows after that.
    There are two possible formats (types):
        Type 0: uncompressed 2 byte data series
        Type 1: 3 byte address, 2 byte data

    The unpacked layout for a data file is a 2,000,000 bytes array.
    Two bytes are occupied for every number.
    First byte (b1) is organised as (1 bit fill indicator, 5 bit service area code, 2 bit phone type)
    Second byte (b2) is organised as (7 bit preferences, 1 bit opstype)
*/

const DATA_DIR: &'static str = "/opt/data/ncpr";

// filename for given prefix
macro_rules! filename {
    ( $x:expr ) => { format!("{}{}{}.dat", DATA_DIR, MAIN_SEPARATOR, $x) };
}

fn serialize(s: u8, p: &str, o: &str, pt: u8) -> (u8, u8) {
    let mut b1 = 1 << 7;  // indicator bit
    let mut b2 = 0;

    assert!(s > 0 && s < 32);
    b1 |=  s << 2;

    assert!(pt > 0 && pt < 4);
    b1 |= pt;

    if p != "0" {
        for i in p.split('#').filter_map(|x| if x.is_empty() { None } else { Some(x.parse::<u8>().unwrap()) }) {
            assert!(i >= 1 && i <= 7);
            b2 |= 1 << i
        }
    }

    if o != "D" {
        assert!(o == "A");
        b2 |= 1;
    }

    (b1, b2)
}

fn deserialize(b1: u8, b2: u8) -> Option<(u8, String, String, u8)> {
    if b1 & 0b1000_0000 == 0 {
        return None;
    }

    Some((
        (b1 & 0b0111_1100) >> 2,
        if b2 & 0b1111_1110 == 0 {
            "0".to_string()
        } else {
            (1..8).filter(|&x| (b2 >> x) & 1 == 1).map(|x| x.to_string()).collect::<Vec<String>>().connect("#")
        },
        if b2 & 1 == 0 {
            "D".to_string()
        } else {
            "A".to_string()
        },
        b1 & 0b0000_0011
    ))
}

fn pack_addr(j: usize) -> (u8, u8, u8) {
    (
        ((j & 0xff0000) >> 16) as u8,
        ((j & 0x00ff00) >> 8) as u8,
        (j & 0x0000ff) as u8
    )
}

fn unpack_addr(b1: u8, b2: u8, b3: u8) -> usize {
    ((b1 as usize) << 16) | ((b2 as usize) << 8) | (b3 as usize)
}

fn load(h: &u32, unpacked: &mut[u8]) -> Result<()> {
    // h is prefix (first 4 digits of phone number)
    assert!(unpacked.len() == 2_000_000);

    let filename = filename!(h);
    if fs::metadata(&filename).is_err() {
        return Ok(());
    }

    let mut f = try!(File::open(filename));
    let mut buf = Vec::with_capacity(2_000_001);
    let bytes_read = try!(f.read_to_end(&mut buf));

    if buf[0] & 1 == 0 {
        assert!((bytes_read - 1) == 2_000_000);
        for i in 1..bytes_read {
            unpacked[i - 1] = buf[i];
        }
        return Ok(());
    }

    assert!((bytes_read - 1) % 5 == 0);
    let mut i = 1;
    while i < bytes_read {
        let j = unpack_addr(buf[i], buf[i + 1], buf[i + 2]);
        unpacked[2 * j] = buf[i + 3];
        unpacked[2 * j + 1] = buf[i + 4];
        i += 5;
    }

    Ok(())
}

fn dump(h: &u32, unpacked: &[u8]) -> Result<()> {
    assert!(unpacked.len() == 2_000_000);

    let mut i = 0;
    let mut filled_count = 0;
    while i < 2_000_000 {
        if unpacked[i] & 0b1000_0000 != 0 {
            filled_count += 1;
        }
        i += 2;
    }

    let filename = filename!(h);
    let mut f = File::create(filename).unwrap();
    if filled_count >= 400_000 {  // >= 40%
        try!(f.write(&mut [0u8]));
        try!(f.write(unpacked));
        try!(f.flush());
        return Ok(());
    }

    let mut buf = vec![0u8;filled_count * 5];
    let mut j = 0;
    i = 0;
    while i < 2_000_000 {
        if unpacked[i] & 0b1000_0000 != 0 {
            let (b1, b2, b3) = pack_addr(i / 2);
            buf[j] = b1;
            buf[j + 1] = b2;
            buf[j + 2] = b3;
            buf[j + 3] = unpacked[i];
            buf[j + 4] = unpacked[i + 1];
            j += 5;
        }
        i += 2;
    }

    try!(f.write(&mut [1u8]));
    try!(f.write(&buf[..]));
    try!(f.flush());
    Ok(())
}

// search in appropriate file
fn search(pn: &str) -> Option<(u8, String, String, u8)> {
    // binary search for type 2
    fn binary_search(key: usize, data: &[u8], low: usize, high: usize) -> Option<(u8, String, String, u8)> {
        // inclusive of low and high index for searching
        if low > high {
            return None;
        }

        let mid = (low + high) / 2;
        let mid_key = unpack_addr(data[mid * 5], data[mid * 5 + 1], data[mid * 5 + 2]);

        if key < mid_key {
            binary_search(key, data, low, mid - 1)
        } else if key == mid_key {
            deserialize(data[mid * 5 + 3], data[mid * 5 + 4])
        } else {
            binary_search(key, data, mid + 1, high)
        }
    }

    assert!(pn.len() == 10);
    let h = pn[..4].parse::<u32>().unwrap();
    let t = pn[4..].parse::<usize>().unwrap();

    if t >= 1_000_000 {
        return None;
    }

    let filename = filename!(h);
    if fs::metadata(&filename).is_err() {
        return None;
    }

    let mut f = File::open(filename).unwrap();
    let mut buf = Vec::with_capacity(2_000_001);
    let bytes_read = f.read_to_end(&mut buf).unwrap();

    if buf[0] & 1 == 0 {
        assert!((bytes_read - 1) == 2_000_000);
        return deserialize(buf[2 * t + 1], buf[2 * t + 2]);
    }

    assert!((bytes_read - 1) % 5 == 0);
    binary_search(t, &buf[1..], 0, (bytes_read - 1) / 5 - 1)
}

// patch from stdin
fn patch() {
    let mut rdr = csv::Reader::from_reader(io::stdin());

    let mut head_map: HashMap<u32, Vec<(usize, (u8, u8))>> = HashMap::with_capacity(10_000);
    for record in rdr.decode() {
        let (s, pn, p, o, pt): (u8, String, String, String, u8) = record.unwrap();
        if pn.as_bytes().iter().filter(|&x| *x >= 0x30 && *x <= 0x39).count() != 10 {
            println!("ignoring invalid number {}", pn);
            continue;
        }
        let h = pn[..4].parse::<u32>().unwrap();
        let t = pn[4..].parse::<usize>().unwrap();
        if !head_map.contains_key(&h) {
            head_map.insert(h, Vec::new());
        }
        head_map.get_mut(&h).unwrap().push((t, serialize(s, &p, &o, pt)));
    }

    let total = head_map.len();
    println!("patching {} files", total);

    //let mut i = 0;
    let mut count = 0;
    for (h, items) in head_map.iter() {
        //println!("[{}/{}] processing {}", i + 1, total, h);

        let mut unpacked = [0u8;2_000_000];
        let load_result = load(h, &mut unpacked);
        assert!(load_result.is_ok());

        for &(t, (b1, b2)) in items {
            unpacked[2 * t] = b1;
            unpacked[2 * t + 1] = b2;
            count += 1;
        }

        let dump_result = dump(h, &unpacked);
        assert!(dump_result.is_ok());

        //i += 1;
    }
    println!("patched a total of {} records", count);
}

fn main() {
    let program_name = ::std::env::args().nth(0).unwrap();
    let command = ::std::env::args().nth(1);
    match command {
        Some(ref s) if *s == "patch".to_string() => {
            println!("reading from stdin");
            patch();
        },
        Some(ref s) if *s == "search".to_string() => {
            let keyword = ::std::env::args().nth(2);
            if keyword == None {
                println!("missing argument phone number");
            } else {
                println!("{:?}", search(&keyword.unwrap()));
            }
        }
        _ => {
            println!("Usage: {} [patch|[search number]]", program_name);
        }
    }
}
<commit_msg>replaced deprecated function for slice concatenation<commit_after>extern crate csv;

use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io;
use std::io::Read;
use std::io::Result;
use std::io::Write;
use std::path::MAIN_SEPARATOR;

/*
    Files are arranged and named from first 4 digits of number.

    Data is boxed in a file. The first byte of file indicates the type. Data follows after that.
    There are two possible formats (types):
        Type 0: uncompressed 2 byte data series
        Type 1: 3 byte address, 2 byte data

    The unpacked layout for a data file is a 2,000,000 bytes array.
    Two bytes are occupied for every number.
    First byte (b1) is organised as (1 bit fill indicator, 5 bit service area code, 2 bit phone type)
    Second byte (b2) is organised as (7 bit preferences, 1 bit opstype)
*/

const DATA_DIR: &'static str = "/opt/data/ncpr";

// filename for given prefix
macro_rules! filename {
    ( $x:expr ) => { format!("{}{}{}.dat", DATA_DIR, MAIN_SEPARATOR, $x) };
}

fn serialize(s: u8, p: &str, o: &str, pt: u8) -> (u8, u8) {
    let mut b1 = 1 << 7;  // indicator bit
    let mut b2 = 0;

    assert!(s > 0 && s < 32);
    b1 |=  s << 2;

    assert!(pt > 0 && pt < 4);
    b1 |= pt;

    if p != "0" {
        for i in p.split('#').filter_map(|x| if x.is_empty() { None } else { Some(x.parse::<u8>().unwrap()) }) {
            assert!(i >= 1 && i <= 7);
            b2 |= 1 << i
        }
    }

    if o != "D" {
        assert!(o == "A");
        b2 |= 1;
    }

    (b1, b2)
}

fn deserialize(b1: u8, b2: u8) -> Option<(u8, String, String, u8)> {
    if b1 & 0b1000_0000 == 0 {
        return None;
    }

    Some((
        (b1 & 0b0111_1100) >> 2,
        if b2 & 0b1111_1110 == 0 {
            "0".to_string()
        } else {
            (1..8).filter(|&x| (b2 >> x) & 1 == 1).map(|x| x.to_string()).collect::<Vec<String>>().join("#")
        },
        if b2 & 1 == 0 {
            "D".to_string()
        } else {
            "A".to_string()
        },
        b1 & 0b0000_0011
    ))
}

fn pack_addr(j: usize) -> (u8, u8, u8) {
    (
        ((j & 0xff0000) >> 16) as u8,
        ((j & 0x00ff00) >> 8) as u8,
        (j & 0x0000ff) as u8
    )
}

fn unpack_addr(b1: u8, b2: u8, b3: u8) -> usize {
    ((b1 as usize) << 16) | ((b2 as usize) << 8) | (b3 as usize)
}

fn load(h: &u32, unpacked: &mut[u8]) -> Result<()> {
    // h is prefix (first 4 digits of phone number)
    assert!(unpacked.len() == 2_000_000);

    let filename = filename!(h);
    if fs::metadata(&filename).is_err() {
        return Ok(());
    }

    let mut f = try!(File::open(filename));
    let mut buf = Vec::with_capacity(2_000_001);
    let bytes_read = try!(f.read_to_end(&mut buf));

    if buf[0] & 1 == 0 {
        assert!((bytes_read - 1) == 2_000_000);
        for i in 1..bytes_read {
            unpacked[i - 1] = buf[i];
        }
        return Ok(());
    }

    assert!((bytes_read - 1) % 5 == 0);
    let mut i = 1;
    while i < bytes_read {
        let j = unpack_addr(buf[i], buf[i + 1], buf[i + 2]);
        unpacked[2 * j] = buf[i + 3];
        unpacked[2 * j + 1] = buf[i + 4];
        i += 5;
    }

    Ok(())
}

fn dump(h: &u32, unpacked: &[u8]) -> Result<()> {
    assert!(unpacked.len() == 2_000_000);

    let mut i = 0;
    let mut filled_count = 0;
    while i < 2_000_000 {
        if unpacked[i] & 0b1000_0000 != 0 {
            filled_count += 1;
        }
        i += 2;
    }

    let filename = filename!(h);
    let mut f = File::create(filename).unwrap();
    if filled_count >= 400_000 {  // >= 40%
        try!(f.write(&mut [0u8]));
        try!(f.write(unpacked));
        try!(f.flush());
        return Ok(());
    }

    let mut buf = vec![0u8;filled_count * 5];
    let mut j = 0;
    i = 0;
    while i < 2_000_000 {
        if unpacked[i] & 0b1000_0000 != 0 {
            let (b1, b2, b3) = pack_addr(i / 2);
            buf[j] = b1;
            buf[j + 1] = b2;
            buf[j + 2] = b3;
            buf[j + 3] = unpacked[i];
            buf[j + 4] = unpacked[i + 1];
            j += 5;
        }
        i += 2;
    }

    try!(f.write(&mut [1u8]));
    try!(f.write(&buf[..]));
    try!(f.flush());
    Ok(())
}

// search in appropriate file
fn search(pn: &str) -> Option<(u8, String, String, u8)> {
    // binary search for type 2
    fn binary_search(key: usize, data: &[u8], low: usize, high: usize) -> Option<(u8, String, String, u8)> {
        // inclusive of low and high index for searching
        if low > high {
            return None;
        }

        let mid = (low + high) / 2;
        let mid_key = unpack_addr(data[mid * 5], data[mid * 5 + 1], data[mid * 5 + 2]);

        if key < mid_key {
            binary_search(key, data, low, mid - 1)
        } else if key == mid_key {
            deserialize(data[mid * 5 + 3], data[mid * 5 + 4])
        } else {
            binary_search(key, data, mid + 1, high)
        }
    }

    assert!(pn.len() == 10);
    let h = pn[..4].parse::<u32>().unwrap();
    let t = pn[4..].parse::<usize>().unwrap();

    if t >= 1_000_000 {
        return None;
    }

    let filename = filename!(h);
    if fs::metadata(&filename).is_err() {
        return None;
    }

    let mut f = File::open(filename).unwrap();
    let mut buf = Vec::with_capacity(2_000_001);
    let bytes_read = f.read_to_end(&mut buf).unwrap();

    if buf[0] & 1 == 0 {
        assert!((bytes_read - 1) == 2_000_000);
        return deserialize(buf[2 * t + 1], buf[2 * t + 2]);
    }

    assert!((bytes_read - 1) % 5 == 0);
    binary_search(t, &buf[1..], 0, (bytes_read - 1) / 5 - 1)
}

// patch from stdin
fn patch() {
    let mut rdr = csv::Reader::from_reader(io::stdin());

    let mut head_map: HashMap<u32, Vec<(usize, (u8, u8))>> = HashMap::with_capacity(10_000);
    for record in rdr.decode() {
        let (s, pn, p, o, pt): (u8, String, String, String, u8) = record.unwrap();
        if pn.as_bytes().iter().filter(|&x| *x >= 0x30 && *x <= 0x39).count() != 10 {
            println!("ignoring invalid number {}", pn);
            continue;
        }
        let h = pn[..4].parse::<u32>().unwrap();
        let t = pn[4..].parse::<usize>().unwrap();
        if !head_map.contains_key(&h) {
            head_map.insert(h, Vec::new());
        }
        head_map.get_mut(&h).unwrap().push((t, serialize(s, &p, &o, pt)));
    }

    let total = head_map.len();
    println!("patching {} files", total);

    //let mut i = 0;
    let mut count = 0;
    for (h, items) in head_map.iter() {
        //println!("[{}/{}] processing {}", i + 1, total, h);

        let mut unpacked = [0u8;2_000_000];
        let load_result = load(h, &mut unpacked);
        assert!(load_result.is_ok());

        for &(t, (b1, b2)) in items {
            unpacked[2 * t] = b1;
            unpacked[2 * t + 1] = b2;
            count += 1;
        }

        let dump_result = dump(h, &unpacked);
        assert!(dump_result.is_ok());

        //i += 1;
    }
    println!("patched a total of {} records", count);
}

fn main() {
    let program_name = ::std::env::args().nth(0).unwrap();
    let command = ::std::env::args().nth(1);
    match command {
        Some(ref s) if *s == "patch".to_string() => {
            println!("reading from stdin");
            patch();
        },
        Some(ref s) if *s == "search".to_string() => {
            let keyword = ::std::env::args().nth(2);
            if keyword == None {
                println!("missing argument phone number");
            } else {
                println!("{:?}", search(&keyword.unwrap()));
            }
        }
        _ => {
            println!("Usage: {} [patch|[search number]]", program_name);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Alacritty - The GPU Enhanced Terminal
#![feature(plugin)]
#![cfg_attr(feature = "clippy", plugin(clippy))]

#[macro_use]
extern crate alacritty;

use std::error::Error;
use std::sync::Arc;

use alacritty::cli;
use alacritty::config::{self, Config};
use alacritty::display::Display;
use alacritty::event;
use alacritty::event_loop::{self, EventLoop};
use alacritty::sync::FairMutex;
use alacritty::term::{Term};
use alacritty::tty::{self, process_should_exit};

fn main() {
    // Load configuration
    let config = match Config::load() {
        // Error loading config
        Err(err) => match err {
            // Use default config when not found
            config::Error::NotFound => {
                err_println!("Config file not found; using defaults");
                Config::default()
            },

            // If there's a problem with the config file, print an error
            // and exit.
            _ => die!("{}", err),
        },

        // Successfully loaded config from file
        Ok(config) => config
    };

    // Load command line options
    let options = cli::Options::load();

    // Run alacritty
    if let Err(err) = run(config, options) {
        die!("{}", err);
    }

    println!("Goodbye");
}

/// Run Alacritty
///
/// Creates a window, the terminal state, pty, I/O event loop, input processor,
/// config change monitor, and runs the main display loop.
fn run(mut config: Config, options: cli::Options) -> Result<(), Box<Error>> {
    // Create a display.
    //
    // The display manages a window and can draw the terminal
    let mut display = Display::new(&config, &options)?;

    // Create the terminal
    //
    // This object contains all of the state about what's being displayed. It's
    // wrapped in a clonable mutex since both the I/O loop and display need to
    // access it.
    let terminal = Arc::new(FairMutex::new(Term::new(display.size().to_owned())));

    // Create the pty
    //
    // The pty forks a process to run the shell on the slave side of the
    // pseudoterminal. A file descriptor for the master side is retained for
    // reading/writing to the shell.
    let mut pty = tty::new(display.size());

    // Create the pseudoterminal I/O loop
    //
    // pty I/O is ran on another thread as to not occupy cycles used by the
    // renderer and input processing. Note that access to the terminal state is
    // synchronized since the I/O loop updates the state, and the display
    // consumes it periodically.
    let event_loop = EventLoop::new(
        terminal.clone(),
        display.notifier(),
        pty.reader(),
        options.ref_test,
    );

    // The event loop channel allows write requests from the event processor
    // to be sent to the loop and ultimately written to the pty.
    let loop_tx = event_loop.channel();

    // Event processor
    //
    // Need the Rc<RefCell<_>> here since a ref is shared in the resize callback
    let mut processor = event::Processor::new(
        event_loop::Notifier(loop_tx),
        terminal.clone(),
        display.resize_channel(),
        &config,
        options.ref_test,
    );

    // Create a config monitor when config was loaded from path
    //
    // The monitor watches the config file for changes and reloads it. Pending
    // config changes are processed in the main loop.
    let config_monitor = config.path()
        .map(|path| config::Monitor::new(path, display.notifier()));

    // Kick off the I/O thread
    let io_thread = event_loop.spawn(None);

    // Main display loop
    loop {
        // Process input and window events
        let wakeup_request = processor.process_events(display.window());

        // Handle config reloads
        let config_updated = config_monitor.as_ref()
            .and_then(|monitor| monitor.pending_config())
            .map(|new_config| {
                config = new_config;
                display.update_config(&config);
                processor.update_config(&config);
                true
            }).unwrap_or(false);

        // Maybe draw the terminal
        let mut terminal = terminal.lock();
        if wakeup_request || config_updated {
            // Handle pending resize events
            //
            // The second argument is a list of types that want to be notified
            // of display size changes.
            display.handle_resize(&mut terminal, &mut [&mut pty, &mut processor]);

            // Draw the current state of the terminal
            display.draw(terminal, &config, &processor.selection);
        }

        // Begin shutdown if the flag was raised.
        if process_should_exit() {
            break;
        }
    }

    // FIXME patch notify library to have a shutdown method
    // config_reloader.join().ok();

    // Wait for the I/O thread thread to finish
    let _ = io_thread.join();

    Ok(())
}
<commit_msg>Minor cleanup in main()<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Alacritty - The GPU Enhanced Terminal
#![feature(plugin)]
#![cfg_attr(feature = "clippy", plugin(clippy))]

#[macro_use]
extern crate alacritty;

use std::error::Error;
use std::sync::Arc;

use alacritty::cli;
use alacritty::config::{self, Config};
use alacritty::display::Display;
use alacritty::event;
use alacritty::event_loop::{self, EventLoop};
use alacritty::sync::FairMutex;
use alacritty::term::{Term};
use alacritty::tty::{self, process_should_exit};

fn main() {
    // Load configuration
    let config = Config::load().unwrap_or_else(|err| {
        match err {
            // Use default config when not found
            config::Error::NotFound => {
                err_println!("Config file not found; using defaults");
                Config::default()
            },

            // If there's a problem with the config file, print an error
            // and exit.
            _ => die!("{}", err),
        }
    });

    // Load command line options
    let options = cli::Options::load();

    // Run alacritty
    if let Err(err) = run(config, options) {
        die!("{}", err);
    }

    println!("Goodbye");
}

/// Run Alacritty
///
/// Creates a window, the terminal state, pty, I/O event loop, input processor,
/// config change monitor, and runs the main display loop.
fn run(mut config: Config, options: cli::Options) -> Result<(), Box<Error>> {
    // Create a display.
    //
    // The display manages a window and can draw the terminal
    let mut display = Display::new(&config, &options)?;

    // Create the terminal
    //
    // This object contains all of the state about what's being displayed. It's
    // wrapped in a clonable mutex since both the I/O loop and display need to
    // access it.
    let terminal = Arc::new(FairMutex::new(Term::new(display.size().to_owned())));

    // Create the pty
    //
    // The pty forks a process to run the shell on the slave side of the
    // pseudoterminal. A file descriptor for the master side is retained for
    // reading/writing to the shell.
    let mut pty = tty::new(display.size());

    // Create the pseudoterminal I/O loop
    //
    // pty I/O is ran on another thread as to not occupy cycles used by the
    // renderer and input processing. Note that access to the terminal state is
    // synchronized since the I/O loop updates the state, and the display
    // consumes it periodically.
    let event_loop = EventLoop::new(
        terminal.clone(),
        display.notifier(),
        pty.reader(),
        options.ref_test,
    );

    // The event loop channel allows write requests from the event processor
    // to be sent to the loop and ultimately written to the pty.
    let loop_tx = event_loop.channel();

    // Event processor
    //
    // Need the Rc<RefCell<_>> here since a ref is shared in the resize callback
    let mut processor = event::Processor::new(
        event_loop::Notifier(loop_tx),
        terminal.clone(),
        display.resize_channel(),
        &config,
        options.ref_test,
    );

    // Create a config monitor when config was loaded from path
    //
    // The monitor watches the config file for changes and reloads it. Pending
    // config changes are processed in the main loop.
    let config_monitor = config.path()
        .map(|path| config::Monitor::new(path, display.notifier()));

    // Kick off the I/O thread
    let io_thread = event_loop.spawn(None);

    // Main display loop
    loop {
        // Process input and window events
        let wakeup_request = processor.process_events(display.window());

        // Handle config reloads
        let config_updated = config_monitor.as_ref()
            .and_then(|monitor| monitor.pending_config())
            .map(|new_config| {
                config = new_config;
                display.update_config(&config);
                processor.update_config(&config);
                true
            }).unwrap_or(false);

        // Maybe draw the terminal
        let mut terminal = terminal.lock();
        if wakeup_request || config_updated {
            // Handle pending resize events
            //
            // The second argument is a list of types that want to be notified
            // of display size changes.
            display.handle_resize(&mut terminal, &mut [&mut pty, &mut processor]);

            // Draw the current state of the terminal
            display.draw(terminal, &config, &processor.selection);
        }

        // Begin shutdown if the flag was raised.
        if process_should_exit() {
            break;
        }
    }

    // FIXME patch notify library to have a shutdown method
    // config_reloader.join().ok();

    // Wait for the I/O thread thread to finish
    let _ = io_thread.join();

    Ok(())
}
<|endoftext|>


<commit_before>pub mod api;
pub mod db;
pub mod indexer;
pub mod model;
pub mod opening;
pub mod util;

use crate::{
    api::{Error, GameRow, GameRowWithUci, PersonalMoveRow, PersonalQuery, PersonalResponse},
    db::Database,
    indexer::{IndexerOpt, IndexerStub},
    model::{AnnoLichess, PersonalKeyBuilder},
    opening::Openings,
};
use axum::{
    extract::{Extension, Path, Query},
    handler::get,
    http::StatusCode,
    response::Json,
    AddExtensionLayer, Router,
};
use clap::Clap;
use shakmaty::{fen::Fen, variant::VariantPosition, zobrist::Zobrist, CastlingMode};
use std::{net::SocketAddr, path::PathBuf, sync::Arc};

#[derive(Clap)]
struct Opt {
    #[clap(long = "bind", default_value = "127.0.0.1:9000")]
    bind: SocketAddr,
    #[clap(long = "db", default_value = "_db")]
    db: PathBuf,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

#[tokio::main]
async fn main() {
    env_logger::init();

    let opt = Opt::parse();

    let openings: &'static Openings = Box::leak(Box::new(Openings::new()));
    let db = Arc::new(Database::open(opt.db).expect("db"));
    let (indexer, join_handle) = IndexerStub::spawn(db.clone(), opt.indexer);

    let app = Router::new()
        .route("/personal", get(personal))
        .route("/admin/game/:prop", get(game_property))
        .route("/admin/personal/:prop", get(personal_property))
        .route("/admin/:prop", get(db_property))
        .layer(AddExtensionLayer::new(openings))
        .layer(AddExtensionLayer::new(db))
        .layer(AddExtensionLayer::new(indexer));

    axum::Server::bind(&opt.bind)
        .serve(app.into_make_service())
        .with_graceful_shutdown(async {
            tokio::signal::ctrl_c().await.expect("wait for ctrl-c");
        })
        .await
        .expect("bind");

    join_handle.await.expect("indexer");
}

async fn db_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .db_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn game_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .game_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn personal_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .personal_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn personal(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PersonalQuery>,
) -> Result<Json<PersonalResponse>, Error> {
    if query.update {
        let _status = indexer.index_player(query.player.clone()).await?;
    }

    let variant = query.variant.into();

    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = PersonalKeyBuilder::with_user_pov(&query.player.into(), query.color)
        .with_zobrist(variant, pos.zobrist_hash());

    let queryable = db.queryable();
    let filtered = queryable
        .get_personal(key, AnnoLichess::from_year(query.since))
        .expect("get personal")
        .prepare(pos.into_inner(), query.filter);

    Ok(Json(PersonalResponse {
        total: filtered.total,
        moves: filtered
            .moves
            .into_iter()
            .map(|row| PersonalMoveRow {
                uci: row.uci,
                san: row.san,
                stats: row.stats,
                game: row.game.and_then(|id| {
                    queryable
                        .get_game_info(id)
                        .expect("get game")
                        .map(|info| GameRow { id, info })
                }),
            })
            .collect(),
        recent_games: filtered
            .recent_games
            .into_iter()
            .flat_map(|(uci, id)| {
                queryable
                    .get_game_info(id)
                    .expect("get game")
                    .map(|info| GameRowWithUci {
                        uci,
                        row: GameRow { id, info },
                    })
            })
            .collect(),
        opening,
    }))
}
<commit_msg>optimize routing order<commit_after>pub mod api;
pub mod db;
pub mod indexer;
pub mod model;
pub mod opening;
pub mod util;

use crate::{
    api::{Error, GameRow, GameRowWithUci, PersonalMoveRow, PersonalQuery, PersonalResponse},
    db::Database,
    indexer::{IndexerOpt, IndexerStub},
    model::{AnnoLichess, PersonalKeyBuilder},
    opening::Openings,
};
use axum::{
    extract::{Extension, Path, Query},
    handler::get,
    http::StatusCode,
    response::Json,
    AddExtensionLayer, Router,
};
use clap::Clap;
use shakmaty::{fen::Fen, variant::VariantPosition, zobrist::Zobrist, CastlingMode};
use std::{net::SocketAddr, path::PathBuf, sync::Arc};

#[derive(Clap)]
struct Opt {
    #[clap(long = "bind", default_value = "127.0.0.1:9000")]
    bind: SocketAddr,
    #[clap(long = "db", default_value = "_db")]
    db: PathBuf,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

#[tokio::main]
async fn main() {
    env_logger::init();

    let opt = Opt::parse();

    let openings: &'static Openings = Box::leak(Box::new(Openings::new()));
    let db = Arc::new(Database::open(opt.db).expect("db"));
    let (indexer, join_handle) = IndexerStub::spawn(db.clone(), opt.indexer);

    let app = Router::new()
        .route("/admin/:prop", get(db_property))
        .route("/admin/game/:prop", get(game_property))
        .route("/admin/personal/:prop", get(personal_property))
        .route("/personal", get(personal))
        .layer(AddExtensionLayer::new(openings))
        .layer(AddExtensionLayer::new(db))
        .layer(AddExtensionLayer::new(indexer));

    axum::Server::bind(&opt.bind)
        .serve(app.into_make_service())
        .with_graceful_shutdown(async {
            tokio::signal::ctrl_c().await.expect("wait for ctrl-c");
        })
        .await
        .expect("bind");

    join_handle.await.expect("indexer");
}

async fn db_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .db_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn game_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .game_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn personal_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .personal_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn personal(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PersonalQuery>,
) -> Result<Json<PersonalResponse>, Error> {
    if query.update {
        let _status = indexer.index_player(query.player.clone()).await?;
    }

    let variant = query.variant.into();

    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = PersonalKeyBuilder::with_user_pov(&query.player.into(), query.color)
        .with_zobrist(variant, pos.zobrist_hash());

    let queryable = db.queryable();
    let filtered = queryable
        .get_personal(key, AnnoLichess::from_year(query.since))
        .expect("get personal")
        .prepare(pos.into_inner(), query.filter);

    Ok(Json(PersonalResponse {
        total: filtered.total,
        moves: filtered
            .moves
            .into_iter()
            .map(|row| PersonalMoveRow {
                uci: row.uci,
                san: row.san,
                stats: row.stats,
                game: row.game.and_then(|id| {
                    queryable
                        .get_game_info(id)
                        .expect("get game")
                        .map(|info| GameRow { id, info })
                }),
            })
            .collect(),
        recent_games: filtered
            .recent_games
            .into_iter()
            .flat_map(|(uci, id)| {
                queryable
                    .get_game_info(id)
                    .expect("get game")
                    .map(|info| GameRowWithUci {
                        uci,
                        row: GameRow { id, info },
                    })
            })
            .collect(),
        opening,
    }))
}
<|endoftext|>


<commit_before>// Don't show a console window when running on Windows.
#![windows_subsystem = "windows"]

#[macro_use]
extern crate calx;
extern crate display;
extern crate euclid;
extern crate glium;
extern crate rand;
extern crate scancode;
extern crate vitral;
extern crate world;

pub mod game_loop;

use display::Backend;
use game_loop::GameLoop;
use rand::Rng;
use world::World;

pub fn main() {
    let mut backend = Backend::start(640, 360, "Magog").expect("Failed to start rendering backend");

    let seed = rand::thread_rng().gen();
    // Print out the seed in case worldgen has a bug and we want to debug stuff with the same seed.
    println!("Seed: {}", seed);

    let mut game = GameLoop::new(&mut backend, World::new(seed));

    while game.draw(&mut backend) {}
}
<commit_msg>FPS-locked main loop<commit_after>// Don't show a console window when running on Windows.
#![windows_subsystem = "windows"]

#[macro_use]
extern crate calx;
extern crate display;
extern crate euclid;
extern crate glium;
extern crate rand;
extern crate scancode;
extern crate vitral;
extern crate world;

pub mod game_loop;

use calx::TimestepLoop;
use display::Backend;
use game_loop::GameLoop;
use rand::Rng;
use std::thread;
use std::time::Duration;
use world::World;

pub fn main() {
    const FPS: f64 = 30.0;

    let mut timestep = TimestepLoop::new(1.0 / FPS);

    let mut backend = Backend::start(640, 360, "Magog").expect("Failed to start rendering backend");

    let seed = rand::thread_rng().gen();
    // Print out the seed in case worldgen has a bug and we want to debug stuff with the same seed.
    println!("Seed: {}", seed);

    let mut game = GameLoop::new(&mut backend, World::new(seed));

    'gameloop: loop {
        while timestep.should_update() {
            if !game.draw(&mut backend) {
                break 'gameloop;
            }
        }

        let free_time = timestep.time_until_update();
        if free_time > 0.0 {
            thread::sleep(Duration::from_millis((free_time * 1000.0) as u64));
        }

        timestep.observe_render();
    }
}
<|endoftext|>


<commit_before>#![feature(thread_local)]
#[macro_use]
extern crate log;
#[macro_use]
extern crate arrayref;
#[macro_use]
extern crate lazy_static;
extern crate env_logger;
extern crate clap;

mod arch;
mod utils;
mod loaders;
// use arch::memory::Memory;
// use arch::cpu::CPU;

use log::{LogLevelFilter, SetLoggerError};
use env_logger::LogBuilder;
use std::fs::File;
use std::path::PathBuf;
use loaders::loaders_factory::LoadersFactory;
use arch::rom_structs::Header;
use clap::{App, Arg};
use arch::instrs::instr_table::disassemble_instr;

fn init_logger() -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    builder.filter(None, LogLevelFilter::Info);
    builder.init()
}

fn load_rom(buf: &[u8]) -> Result<(Header, &[u8]), String> {
    let header = Header::from_bytes(array_ref![buf[0..16], 0, 16]);

    if &header.header != "NES\x1A".as_bytes() {
        return Err("Invalid header!".to_owned());
    }

    info!("Rom has trainer? {}", header.has_trainer());

    let prg_start = 16 + (if header.has_trainer() { 512 } else { 0 }) as usize;
    let prg_end = prg_start + header.prg_rom_size();

    info!("PRG ROM from {:#X} to {:#X}", prg_start, prg_end);
    info!("Number of PRG banks: {}", header.prg_rom_banks());

    info!("Mapper: {}", header.mapping_number());

    let rom: &[u8] = &buf[prg_start..prg_end];

    Ok((header, rom))
}

fn disassemble_rom(_: &Header, rom: &[u8]) {
    let mut cnt : usize = 0;
    let mut last = 0;

    while cnt < rom.len() {
        let rem = cnt >> 14;
        if cnt == 0 || rem != last {
            last = rem;
            println!("Bank {}", last + 1);
        }
        let (string, cnt_2) = disassemble_instr(&rom, cnt);
        cnt = cnt_2;
        println!("{}", string);
    }
}

pub fn main() {
    let matches = App::new("rustines")
        .version("1.0")
        .author("Davide C. <davide.canton5@gmail.com>")
        .about("NES emulator written in Rust")
        .arg(
            Arg::with_name("disassemble")
                .short("d")
                .help("Disassemble ROM"),
        )
        .arg(
            Arg::with_name("INPUT")
                .help("Sets the input rom file to use")
                .required(true)
                .index(1),
        )
        .get_matches();

    if init_logger().is_err() {
        eprintln!("Failed to initialize logger.");
        return;
    }

    let disassemble = matches.occurrences_of("disassemble") > 0;
    let rom_name = matches.value_of("INPUT").unwrap();

    let file_path = PathBuf::from(rom_name);

    info!("Disassemble: {}", disassemble);
    info!("Using input file: {}", rom_name);

    let ext = match file_path.extension() {
        Some(ext) => ext.to_str().unwrap_or(""),
        None => "",
    };

    let mut file = File::open(&file_path).expect("Failed to open file");

    let loader = LoadersFactory::decode(ext);

    info!("Selected loader {}", loader.name());

    let buf = loader.load_rom(&mut file).expect("Failed to load ROM");
    match load_rom(&buf) {
        Ok((header, rom)) => {
            info!("ROM size: {:#X}", rom.len());

            if disassemble {
                disassemble_rom(&header, &rom);
            } else {
                // TODO execute ROM
            }
        }
        Err(e) => {
            eprintln!("Errore {}", e);
        }
    }
}
<commit_msg>Fix test v2<commit_after>#[macro_use]
extern crate log;
#[macro_use]
extern crate arrayref;
#[macro_use]
extern crate lazy_static;
extern crate env_logger;
extern crate clap;

mod arch;
mod utils;
mod loaders;
// use arch::memory::Memory;
// use arch::cpu::CPU;

use log::{LogLevelFilter, SetLoggerError};
use env_logger::LogBuilder;
use std::fs::File;
use std::path::PathBuf;
use loaders::loaders_factory::LoadersFactory;
use arch::rom_structs::Header;
use clap::{App, Arg};
use arch::instrs::instr_table::disassemble_instr;

fn init_logger() -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    builder.filter(None, LogLevelFilter::Info);
    builder.init()
}

fn load_rom(buf: &[u8]) -> Result<(Header, &[u8]), String> {
    let header = Header::from_bytes(array_ref![buf[0..16], 0, 16]);

    if &header.header != "NES\x1A".as_bytes() {
        return Err("Invalid header!".to_owned());
    }

    info!("Rom has trainer? {}", header.has_trainer());

    let prg_start = 16 + (if header.has_trainer() { 512 } else { 0 }) as usize;
    let prg_end = prg_start + header.prg_rom_size();

    info!("PRG ROM from {:#X} to {:#X}", prg_start, prg_end);
    info!("Number of PRG banks: {}", header.prg_rom_banks());

    info!("Mapper: {}", header.mapping_number());

    let rom: &[u8] = &buf[prg_start..prg_end];

    Ok((header, rom))
}

fn disassemble_rom(_: &Header, rom: &[u8]) {
    let mut cnt : usize = 0;
    let mut last = 0;

    while cnt < rom.len() {
        let rem = cnt >> 14;
        if cnt == 0 || rem != last {
            last = rem;
            println!("Bank {}", last + 1);
        }
        let (string, cnt_2) = disassemble_instr(&rom, cnt);
        cnt = cnt_2;
        println!("{}", string);
    }
}

pub fn main() {
    let matches = App::new("rustines")
        .version("1.0")
        .author("Davide C. <davide.canton5@gmail.com>")
        .about("NES emulator written in Rust")
        .arg(
            Arg::with_name("disassemble")
                .short("d")
                .help("Disassemble ROM"),
        )
        .arg(
            Arg::with_name("INPUT")
                .help("Sets the input rom file to use")
                .required(true)
                .index(1),
        )
        .get_matches();

    if init_logger().is_err() {
        eprintln!("Failed to initialize logger.");
        return;
    }

    let disassemble = matches.occurrences_of("disassemble") > 0;
    let rom_name = matches.value_of("INPUT").unwrap();

    let file_path = PathBuf::from(rom_name);

    info!("Disassemble: {}", disassemble);
    info!("Using input file: {}", rom_name);

    let ext = match file_path.extension() {
        Some(ext) => ext.to_str().unwrap_or(""),
        None => "",
    };

    let mut file = File::open(&file_path).expect("Failed to open file");

    let loader = LoadersFactory::decode(ext);

    info!("Selected loader {}", loader.name());

    let buf = loader.load_rom(&mut file).expect("Failed to load ROM");
    match load_rom(&buf) {
        Ok((header, rom)) => {
            info!("ROM size: {:#X}", rom.len());

            if disassemble {
                disassemble_rom(&header, &rom);
            } else {
                // TODO execute ROM
            }
        }
        Err(e) => {
            eprintln!("Errore {}", e);
        }
    }
}
<|endoftext|>


<commit_before>use name::*;
use art::*;
use std::hash;
use std::hash::Hash;

/// Nominal, artful lists: Lists with names and articulation points
#[derive(Show,Hash,PartialEq,Eq)]
pub enum List<'x,T> {
    Nil,
    Cons(T, Box<List<'x,T>>),
    Name(Name, Box<List<'x,T>>),
    Art(Art<'x, Box<List<'x,T>>>)
}

pub fn copy<'x,T:'x>(list:List<'x,T>) -> List<'x,T> {
    match list {
        List::Nil         => List::Nil,
        List::Cons(hd,tl) => List::Cons(hd, box copy(*tl)),
        List::Art(art)    => copy(*force(art)),
        List::Name(nm,tl) => {
            let (nm1,nm2) = fork(nm) ;
            let art = nart!(nm1, box copy(*tl)) ;
            List::Name(nm2, box List::Art(art))
        }
    }
}

pub fn map<'x, T:'x, S:'x>
(f:UAr<'x,T,S>, list:List<'x,T>) -> List<'x,S>
{
    match list {
        List::Nil         => List::Nil,
        List::Cons(hd,tl) => List::Cons((*f).call(hd), box map(f,*tl)),
        // - - - - - boilerplate cases - - - - - -
        List::Art(art)    => map(f,*force(art)),
        List::Name(nm,tl) => {
            let (nm1,nm2) = fork(nm) ;
            let art = nart!(nm1, box map(f,*tl));
            List::Name(nm2, box List::Art(art))
        }
    }
}

pub fn contract<'x,F,G,T:'x>
(f:&'x F, g:&'x G, list:List<'x,T>) -> List<'x,T>
where F:Fn(&T,&T) -> bool + 'x,
      G:Fn(T,T) -> T + 'x
{
    match list {
        List::Nil => List::Nil,
        List::Cons(hd1, box list) => {
            match list {
                List::Nil => List::Cons(hd1, box List::Nil),
                List::Cons(hd2, tl) => {
                    if (*f)(&hd1,&hd2) {
                        List::Cons((*g)(hd1,hd2), box contract(f,g,*tl))
                    } else {
                        List::Cons(hd1, box contract(f, g, List::Cons(hd2, tl)))
                    } },
                // - - - - - boilerplate cases - - - - - -
                List::Art(art) => contract(f,g,List::Cons(hd1,force(art))),
                List::Name(nm, tl) => {
                    let (nm1,nm2) = fork(nm) ;
                    let art = nart!(nm1, box contract(f,g,List::Cons(hd1,tl))) ;
                    List::Name(nm2, box List::Art(art))
                }
            }
        },
        // - - - - - boilerplate cases - - - - - -
        List::Art(art)    => contract(f,g,*force(art)),
        List::Name(nm,tl) => {
            let (nm1,nm2) = fork(nm) ;
            let art = nart!(nm1, box contract(f,g,*tl)) ;
            List::Name(nm2, box List::Art(art))
        },
    }
}

pub fn reduce<'x,F:'x,G:'x,T:'x>
(f:&'x F, g:&'x G, list:List<'x,T>) -> Option<T>
where F: Fn(&T,&T) -> bool,
      G: Fn(T, T) -> T,
{
    match list {
        List::Nil => None,
        List::Cons(hd, box list) => {
            match list {
                List::Nil => Some(hd),
                List::Cons(hd2, tl) => {
                    let hd3 = (*g)(hd,hd2) ;
                    let list = contract(f, g, List::Cons(hd3, tl)) ;
                    reduce (f, g, list)
                },
                // - - - - - boilerplate cases - - - - - -
                List::Name(_,tl) => reduce (f, g, *tl),
                List::Art(art)   => reduce (f, g, *force(art)),
            }},
        // - - - - - boilerplate cases - - - - - -
        List::Name(_,tl) => reduce(f, g, *tl),
        List::Art(art)   => reduce(f, g, *force(art)),
    }
}

pub fn merge<'x,T:'x,Ord:'x>
(ord:&'x Ord, list1:List<'x,T>, list2:List<'x,T>) -> List<'x,T>
where Ord: Fn(&T,&T) -> bool
{
    match (list1, list2) {
        (List::Nil, list2) => list2,
        (list1, List::Nil) => list1,
        (List::Cons(hd1,tl1),
         List::Cons(hd2,tl2)) =>
            if (*ord)(&hd1,&hd2) {
                List::Cons(hd1, box merge(ord, *tl1, List::Cons(hd2,tl2)))
            } else {
                List::Cons(hd2, box merge(ord, List::Cons(hd1,tl1), *tl2))
            },
        // - - - - - boilerplate cases - - - - -
        (List::Name(nm,tl), list2) => {
            let (nm1,nm2) = fork(nm) ;
            let art = nart!(nm1, box merge(ord, *tl, list2)) ;
            List::Name(nm2, box List::Art(art))
        },
        (list1, List::Name(nm,tl)) => {
            let (nm1,nm2) = fork(nm) ;
            let art = nart!(nm1, box merge(ord, list1, *tl)) ;
            List::Name(nm2, box List::Art(art))
        },
        (List::Art(art), list2) => merge(ord, *force(art), list2),
        (list1, List::Art(art)) => merge(ord, list1, *force(art)),
    }
}

pub fn singletons<'x,T:'x>
    (nameop:Option<Name>, list:List<'x,T>) -> List<'x,List<'x,T>>
{
    match list {
        List::Nil => List::Nil,
        List::Cons(hd, tl) => {
            List::Cons( match nameop {
                None => List::Cons(hd, box List::Nil),
                Some(nm) => List::Name(nm, box List::Cons(hd, box List::Nil))
            }, box singletons(None,*tl) )
        },
        List::Art(art) => singletons(nameop,*force(art)),
        List::Name(nm, tl) => {
            let (nm1, nm) = fork(nm) ;
            let (nm2, nm3) = fork(nm) ;
            let art = nart!(nm2, box singletons(Some(nm3), *tl)) ;
            List::Name(nm1, box List::Art(art))
        }
    }
}

pub fn mergesort<'x,T:'x,Ord:'x>
(ord:&'x Ord, list:List<'x,T>) -> List<'x,T>
where Ord: Fn(&T,&T) -> bool, T:Hash
{
    let c = move |&: list1:&List<'x,T>,list2:&List<'x,T>|
    hash::hash(list1) < hash::hash(list2);

    let m = move |&: list1:List<'x,T>,list2:List<'x,T>|
    merge(ord,list1,list2);

    match reduce(&c, &m, singletons(None, list)) {
        None => List::Nil,
        Some(list) => list
    }
}

#[test]
pub fn construct_list () {
    let z : List<int> = List::Nil;
    let y : List<int> = List::Cons(1, box z);
    let x : List<int> = List::Art(cell(symbol(format!("two")), box y));
    let l : List<int> = List::Name(symbol(format!("one")), box x);
    println!("constructed list: {}", l);
}

// TODO: Rustup: Fix this:

 /// An iterator over the items in a list.
pub struct ListItems<'x, T: 'x> {
    list: &'x List<'x,T>
}

impl<'x,T> List<'x,T> {
    type Item = &'x T ;
    /// Get an iterator over the items in a list.
    pub fn iter(&'x self) -> ListItems<'x, T> {
        ListItems {
            list: self
        }
    }

}

impl<'x, T:'x> Iterator for ListItems<'x, T> {
    type Item = &'x T ;
    fn next(&mut self) -> Option<&'x T> {
        match *self.list {
            List::Cons(ref hd, ref tl) => {
                self.list = &**tl;
                Some(hd)
            },
            List::Name(_, ref list) => {
                self.list = &**list;
                self.next()
            },
            List::Art(ref art) => {
                self.list = &**force_ref(art);
                self.next()
            },
            List::Nil => None
        }
    }
}

pub enum NameOrContent<T> { Name(Name),Content(T) }

// pub fn clone_iter<'x, 'y, T:'y>
// (iter:&'x mut Iterator where ) -> List<'y,T>
// where T : Clone,

// {
//     match iter.next() {
//         None => List::Nil,
//         Some(x) => match x {
//             NameOrContent::Content(ref hd) => List::Cons(hd.clone(), box clone_iter(iter)),
//             NameOrContent::Name(ref nm) => {
//                 let (nm1,nm2) = fork(nm.clone());
//                 let rest = clone_iter(iter);
//                 List::Name(nm1, box List::Art( cell(nm2, box rest )))
//             }
//         }
//     }
// }

// pub fn from_iter<'x, 'y, T:'y>
// (iter:&'x mut Iterator) -> List<'y,T>
// where T : Clone
// {
//     match iter.next() {
//         None => List::Nil,
//         Some(x) => match x {
//             NameOrContent::Content(hd) => List::Cons(hd, box from_iter(iter)),
//             NameOrContent::Name(nm) => {
//                 let (nm1,nm2) = fork(nm.clone());
//                 let rest = from_iter(iter);
//                 List::Name(nm1, box List::Art( cell(nm2, box rest )))
//             }
//         }
//     }
// }
<commit_msg>fixed performance bug in merge operation<commit_after>use name::*;
use art::*;
use std::hash;
use std::hash::Hash;

/// Nominal, artful lists: Lists with names and articulation points
#[derive(Show,Hash,PartialEq,Eq)]
pub enum List<'x,T> {
    Nil,
    Cons(T, Box<List<'x,T>>),
    Name(Name, Box<List<'x,T>>),
    Art(Art<'x, Box<List<'x,T>>>)
}

pub fn copy<'x,T:'x>(list:List<'x,T>) -> List<'x,T> {
    match list {
        List::Nil         => List::Nil,
        List::Cons(hd,tl) => List::Cons(hd, box copy(*tl)),
        List::Art(art)    => copy(*force(art)),
        List::Name(nm,tl) => {
            let (nm1,nm2) = fork(nm) ;
            let art = nart!(nm1, box copy(*tl)) ;
            List::Name(nm2, box List::Art(art))
        }
    }
}

pub fn map<'x, T:'x, S:'x>
(f:UAr<'x,T,S>, list:List<'x,T>) -> List<'x,S>
{
    match list {
        List::Nil         => List::Nil,
        List::Cons(hd,tl) => List::Cons((*f).call(hd), box map(f,*tl)),
        // - - - - - boilerplate cases - - - - - -
        List::Art(art)    => map(f,*force(art)),
        List::Name(nm,tl) => {
            let (nm1,nm2) = fork(nm) ;
            let art = nart!(nm1, box map(f,*tl));
            List::Name(nm2, box List::Art(art))
        }
    }
}

pub fn contract<'x,F,G,T:'x>
(f:&'x F, g:&'x G, list:List<'x,T>) -> List<'x,T>
where F:Fn(&T,&T) -> bool + 'x,
      G:Fn(T,T) -> T + 'x
{
    match list {
        List::Nil => List::Nil,
        List::Cons(hd1, box list) => {
            match list {
                List::Nil => List::Cons(hd1, box List::Nil),
                List::Cons(hd2, tl) => {
                    if (*f)(&hd1,&hd2) {
                        List::Cons((*g)(hd1,hd2), box contract(f,g,*tl))
                    } else {
                        List::Cons(hd1, box contract(f, g, List::Cons(hd2, tl)))
                    } },
                // - - - - - boilerplate cases - - - - - -
                List::Art(art) => contract(f,g,List::Cons(hd1,force(art))),
                List::Name(nm, tl) => {
                    let (nm1,nm2) = fork(nm) ;
                    let art = nart!(nm1, box contract(f,g,List::Cons(hd1,tl))) ;
                    List::Name(nm2, box List::Art(art))
                }
            }
        },
        // - - - - - boilerplate cases - - - - - -
        List::Art(art)    => contract(f,g,*force(art)),
        List::Name(nm,tl) => {
            let (nm1,nm2) = fork(nm) ;
            let art = nart!(nm1, box contract(f,g,*tl)) ;
            List::Name(nm2, box List::Art(art))
        },
    }
}

pub fn reduce<'x,F:'x,G:'x,T:'x>
(f:&'x F, g:&'x G, list:List<'x,T>) -> Option<T>
where F: Fn(&T,&T) -> bool,
      G: Fn(T, T) -> T,
{
    match list {
        List::Nil => None,
        List::Cons(hd, box list) => {
            match list {
                List::Nil => Some(hd),
                List::Cons(hd2, tl) => {
                    let hd3 = (*g)(hd,hd2) ;
                    let list = contract(f, g, List::Cons(hd3, tl)) ;
                    reduce (f, g, list)
                },
                // - - - - - boilerplate cases - - - - - -
                List::Name(_,tl) => reduce (f, g, *tl),
                List::Art(art)   => reduce (f, g, *force(art)),
            }},
        // - - - - - boilerplate cases - - - - - -
        List::Name(_,tl) => reduce(f, g, *tl),
        List::Art(art)   => reduce(f, g, *force(art)),
    }
}

pub fn merge<'x,T:'x,Ord:'x>
    (ord:&'x Ord,
     nm1:Option<Name>, list1:List<'x,T>,
     nm2:Option<Name>, list2:List<'x,T>) -> List<'x,T>
    where Ord: Fn(&T,&T) -> bool
{
    match (list1, list2) {
        (List::Nil, list2) => list2,
        (list1, List::Nil) => list1,
        (List::Cons(hd1,tl1),
         List::Cons(hd2,tl2)) =>
            if (*ord)(&hd1,&hd2) {
                let list2 = List::Cons(hd2,tl2) ;
                match nm1 {
                    None => List::Cons(hd1, box merge(ord, None, *tl1, nm2, list2)),
                    Some(nm1) => {
                        let (nm11,nm12) = fork(nm1);
                        let art = nart!(nm12, box merge(ord, None, *tl1, nm2, list2)) ;
                        List::Name(nm11, box List::Cons(hd1, box List::Art(art)))
                    }
                }
            } else {
                // "Assert": This case is symmetric to the one above.
                //
                let list1 = List::Cons(hd1,tl1) ;
                match nm2 {
                    None => List::Cons(hd2, box merge(ord, None, *tl2, nm1, list1)),
                    Some(nm2) => {
                        let (nm21,nm22) = fork(nm2);
                        let art = nart!(nm22, box merge(ord, None, *tl2, nm1, list1)) ;
                        List::Name(nm21, box List::Cons(hd2, box List::Art(art)))
                    }
                }

            },
        // - - - - - "boilerplate" cases - - - - -
        (List::Name(nm,tl), list2) => merge(ord, Some(nm), *tl, nm2, list2),
        (list1, List::Name(nm,tl)) => merge(ord, nm1, list1, Some(nm), *tl),
        (List::Art(art), list2) => merge(ord, nm1, *force(art), nm2, list2),
        (list1, List::Art(art)) => merge(ord, nm1, list1, nm2, *force(art)),
    }
}

pub fn singletons<'x,T:'x>
    (nameop:Option<Name>, list:List<'x,T>) -> List<'x,List<'x,T>>
{
    match list {
        List::Nil => List::Nil,
        List::Cons(hd, tl) => {
            List::Cons( match nameop {
                None => List::Cons(hd, box List::Nil),
                Some(nm) => List::Name(nm, box List::Cons(hd, box List::Nil))
            }, box singletons(None,*tl) )
        },
        List::Art(art) => singletons(nameop,*force(art)),
        List::Name(nm, tl) => {
            let (nm1, nm) = fork(nm) ;
            let (nm2, nm3) = fork(nm) ;
            let art = nart!(nm2, box singletons(Some(nm3), *tl)) ;
            List::Name(nm1, box List::Art(art))
        }
    }
}

pub fn mergesort<'x,T:'x,Ord:'x>
(ord:&'x Ord, list:List<'x,T>) -> List<'x,T>
where Ord: Fn(&T,&T) -> bool, T:Hash
{
    let c = move |&: list1:&List<'x,T>,list2:&List<'x,T>|
    hash::hash(list1) < hash::hash(list2);

    let m = move |&: list1:List<'x,T>,list2:List<'x,T>|
    merge(ord,None,list1,None,list2);

    match reduce(&c, &m, singletons(None, list)) {
        None => List::Nil,
        Some(list) => list
    }
}

#[test]
pub fn construct_list () {
    let z : List<int> = List::Nil;
    let y : List<int> = List::Cons(1, box z);
    let x : List<int> = List::Art(cell(symbol(format!("two")), box y));
    let l : List<int> = List::Name(symbol(format!("one")), box x);
    println!("constructed list: {}", l);
}

// TODO: Rustup: Fix this:

 /// An iterator over the items in a list.
pub struct ListItems<'x, T: 'x> {
    list: &'x List<'x,T>
}

impl<'x,T> List<'x,T> {
    type Item = &'x T ;
    /// Get an iterator over the items in a list.
    pub fn iter(&'x self) -> ListItems<'x, T> {
        ListItems {
            list: self
        }
    }

}

impl<'x, T:'x> Iterator for ListItems<'x, T> {
    type Item = &'x T ;
    fn next(&mut self) -> Option<&'x T> {
        match *self.list {
            List::Cons(ref hd, ref tl) => {
                self.list = &**tl;
                Some(hd)
            },
            List::Name(_, ref list) => {
                self.list = &**list;
                self.next()
            },
            List::Art(ref art) => {
                self.list = &**force_ref(art);
                self.next()
            },
            List::Nil => None
        }
    }
}

pub enum NameOrContent<T> { Name(Name),Content(T) }

// pub fn clone_iter<'x, 'y, T:'y>
// (iter:&'x mut Iterator where ) -> List<'y,T>
// where T : Clone,

// {
//     match iter.next() {
//         None => List::Nil,
//         Some(x) => match x {
//             NameOrContent::Content(ref hd) => List::Cons(hd.clone(), box clone_iter(iter)),
//             NameOrContent::Name(ref nm) => {
//                 let (nm1,nm2) = fork(nm.clone());
//                 let rest = clone_iter(iter);
//                 List::Name(nm1, box List::Art( cell(nm2, box rest )))
//             }
//         }
//     }
// }

// pub fn from_iter<'x, 'y, T:'y>
// (iter:&'x mut Iterator) -> List<'y,T>
// where T : Clone
// {
//     match iter.next() {
//         None => List::Nil,
//         Some(x) => match x {
//             NameOrContent::Content(hd) => List::Cons(hd, box from_iter(iter)),
//             NameOrContent::Name(nm) => {
//                 let (nm1,nm2) = fork(nm.clone());
//                 let rest = from_iter(iter);
//                 List::Name(nm1, box List::Art( cell(nm2, box rest )))
//             }
//         }
//     }
// }
<|endoftext|>