<commit_before>use std::collections::HashMap;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
use std::iter::FromIterator;

use common::Solution;

type State = Vec<(i64, bool)>;

#[derive(Default, Debug)]
pub struct Day12 {
    productions: [bool; 32],
}

fn char_bool(c: char) -> bool {
    match c {
        '#' => true,
        '.' => false,
        _ => panic!("Invalid input {}", c)
    }
}

fn print_state(state: &State) -> String {
    let mut buf = String::with_capacity(state.len());
    for &(_, b) in state {
        if b {
            buf.push('#');
        } else {
            buf.push('.');
        }
    }
    buf
}

fn state_from_string(representation: &str, offset: i64) -> State {
    Vec::from_iter(representation.chars()
        .enumerate()
        .map(|(i, c)| (i as i64 + offset, char_bool(c))))
}

impl Day12 {
    pub fn new() -> Self {
        Default::default()
    }

    fn read_input(&mut self, input: &mut Read) -> State {
        let mut state = Vec::new();
        let mut reader = BufReader::new(input);
        {
            let mut line = String::new();
            reader.read_line(&mut line).unwrap();

            for (idx, c) in line.trim().chars().skip("initial state: ".len()).enumerate() {
                state.push((idx as i64, char_bool(c)));
            }
        }

        for line in reader.lines() {
            let line = line.unwrap();

            if line.is_empty() {
                continue;
            }

            let mut index = 0;
            for c in line.chars().take(5) {
                index <<= 1;
                if char_bool(c) {
                    index |= 1;
                }
            }

            self.productions[index] = char_bool(line.chars().last().unwrap());
        }

        state
    }

    fn simulate(&self, state: &State) -> State {
        let mut new_state = Vec::with_capacity(state.len() + 8);
        let mut index = 0;
        let mut last_idx = None;
        for &(idx, b) in state {
            index = (index << 1) & 0x1f;
            if b {
                index |= 1;
            }

            if self.productions[index] {
                new_state.push((idx - 2, true));
            } else if !new_state.is_empty() {
                new_state.push((idx - 2, false));
            }
            last_idx = Some(idx);
        }
        if let Some(idx) = last_idx {
            for i in 1..=4 {
                index = (index << 1) & 0x1f;

                if self.productions[index] {
                    new_state.push((idx + i - 2, true));
                } else if !new_state.is_empty() {
                    new_state.push((idx + i - 2, false));
                }
            }
        }

        let new_len = new_state.len() - new_state.iter().rev()
            .take_while(|&&(_, x)| !x).count();
        new_state.truncate(new_len);

        new_state
    }

    fn simulate_n(&self, mut state: State, n: usize) -> State {
        for _ in 0..n {
            state = self.simulate(&state);
        }
        state
    }

    fn sum(&self, state: &State) -> i64 {
        state.iter()
            .filter(|&&(_, x)| x)
            .map(|&(i, _)| i)
            .sum()
    }
}

impl Solution for Day12 {
    fn part1(&mut self, input: &mut Read) -> String {
        let mut state = self.read_input(input);
        state = self.simulate_n(state, 20);

        format!("{}", self.sum(&state))
    }

    fn part2(&mut self, input: &mut Read) -> String {
        // Note: this is way too slow
        let mut state = self.read_input(input);
        let mut seen = HashMap::new();
        let mut time = 1i64;
        const TARGET_TIME: i64 = 50000000000;

        loop {
            state = self.simulate(&state);
            let &(offset, _) = state.first().unwrap();
            let representation = print_state(&state);
            if let Some((o_offset, o_time)) = seen.get(&representation) {
                let remaining_time = TARGET_TIME - o_time;
                let cycle_length = time - o_time;
                let iterations_skipped = remaining_time / cycle_length;
                let remainder = remaining_time % cycle_length;
                let new_offset = o_offset + iterations_skipped * (offset - o_offset);

                state = state_from_string(&representation, new_offset);
                state = self.simulate_n(state, remainder as usize);

                return format!("{}", self.sum(&state));
            }
            seen.insert(representation, (offset, time));
            time += 1;
        }
    }
}

#[cfg(test)]
mod tests {
    use common::Solution;
    use day12::Day12;

    const SAMPLE_INPUT: &[u8] = include_bytes!("samples/12.txt");

    #[test]
    fn sample_part1() {
        let mut instance = Day12::new();
        assert_eq!("325", instance.part1(&mut SAMPLE_INPUT));
    }
}
<commit_msg>Remove code duplication.<commit_after>use std::collections::HashMap;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
use std::iter::FromIterator;

use common::Solution;

type State = Vec<(i64, bool)>;

#[derive(Default, Debug)]
pub struct Day12 {
    productions: [bool; 32],
}

fn char_bool(c: char) -> bool {
    match c {
        '#' => true,
        '.' => false,
        _ => panic!("Invalid input {}", c)
    }
}

fn print_state(state: &State) -> String {
    let mut buf = String::with_capacity(state.len());
    for &(_, b) in state {
        if b {
            buf.push('#');
        } else {
            buf.push('.');
        }
    }
    buf
}

fn state_from_string(representation: &str, offset: i64) -> State {
    Vec::from_iter(representation.chars()
        .enumerate()
        .map(|(i, c)| (i as i64 + offset, char_bool(c))))
}

impl Day12 {
    pub fn new() -> Self {
        Default::default()
    }

    fn read_input(&mut self, input: &mut Read) -> State {
        let state;
        let mut reader = BufReader::new(input);
        {
            let mut line = String::new();
            reader.read_line(&mut line).unwrap();
            state = state_from_string(&line["initial state:".len()..line.len()].trim(), 0);
        }

        for line in reader.lines() {
            let line = line.unwrap();

            if line.is_empty() {
                continue;
            }

            let mut index = 0;
            for c in line.chars().take(5) {
                index <<= 1;
                if char_bool(c) {
                    index |= 1;
                }
            }

            self.productions[index] = char_bool(line.chars().last().unwrap());
        }

        state
    }

    fn simulate(&self, state: &State) -> State {
        let mut new_state = Vec::with_capacity(state.len() + 8);
        let mut index = 0;
        let mut last_idx = None;
        for &(idx, b) in state {
            index = (index << 1) & 0x1f;
            if b {
                index |= 1;
            }

            if self.productions[index] {
                new_state.push((idx - 2, true));
            } else if !new_state.is_empty() {
                new_state.push((idx - 2, false));
            }
            last_idx = Some(idx);
        }
        if let Some(idx) = last_idx {
            for i in 1..=4 {
                index = (index << 1) & 0x1f;

                if self.productions[index] {
                    new_state.push((idx + i - 2, true));
                } else if !new_state.is_empty() {
                    new_state.push((idx + i - 2, false));
                }
            }
        }

        let new_len = new_state.len() - new_state.iter().rev()
            .take_while(|&&(_, x)| !x).count();
        new_state.truncate(new_len);

        new_state
    }

    fn simulate_n(&self, mut state: State, n: usize) -> State {
        for _ in 0..n {
            state = self.simulate(&state);
        }
        state
    }

    fn sum(&self, state: &State) -> i64 {
        state.iter()
            .filter(|&&(_, x)| x)
            .map(|&(i, _)| i)
            .sum()
    }
}

impl Solution for Day12 {
    fn part1(&mut self, input: &mut Read) -> String {
        let mut state = self.read_input(input);
        state = self.simulate_n(state, 20);

        format!("{}", self.sum(&state))
    }

    fn part2(&mut self, input: &mut Read) -> String {
        // Note: this is way too slow
        let mut state = self.read_input(input);
        let mut seen = HashMap::new();
        let mut time = 1i64;
        const TARGET_TIME: i64 = 50000000000;

        loop {
            state = self.simulate(&state);
            let &(offset, _) = state.first().unwrap();
            let representation = print_state(&state);
            if let Some((o_offset, o_time)) = seen.get(&representation) {
                let remaining_time = TARGET_TIME - o_time;
                let cycle_length = time - o_time;
                let iterations_skipped = remaining_time / cycle_length;
                let remainder = remaining_time % cycle_length;
                let new_offset = o_offset + iterations_skipped * (offset - o_offset);

                state = state_from_string(&representation, new_offset);
                state = self.simulate_n(state, remainder as usize);

                return format!("{}", self.sum(&state));
            }
            seen.insert(representation, (offset, time));
            time += 1;
        }
    }
}

#[cfg(test)]
mod tests {
    use common::Solution;
    use day12::Day12;

    const SAMPLE_INPUT: &[u8] = include_bytes!("samples/12.txt");

    #[test]
    fn sample_part1() {
        let mut instance = Day12::new();
        assert_eq!("325", instance.part1(&mut SAMPLE_INPUT));
    }
}
<|endoftext|>


<commit_before>use std::cmp::Reverse;
use std::collections::hash_map::Entry;
use std::collections::BinaryHeap;
use std::collections::HashMap;
use std::fmt::Display;
use std::io::Read;
use std::mem::swap;

use crate::common::ordered;
use crate::common::LineIter;

type Item = (u32, u32, State);
type Todo = BinaryHeap<Reverse<Item>>;
type Visited = HashMap<State, u32>;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
enum Pod {
    A,
    B,
    C,
    D,
}

impl Pod {
    pub fn cost(self) -> u32 {
        match self {
            Pod::A => 1,
            Pod::B => 10,
            Pod::C => 100,
            Pod::D => 1000,
        }
    }
}

impl TryFrom<char> for Pod {
    type Error = &'static str;

    fn try_from(c: char) -> Result<Self, Self::Error> {
        match c {
            'A' => Ok(Pod::A),
            'B' => Ok(Pod::B),
            'C' => Ok(Pod::C),
            'D' => Ok(Pod::D),
            _ => Err("Invalid pod"),
        }
    }
}

#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
struct State {
    hallway: [Option<Pod>; 11],
    rooms: [[Option<Pod>; 2]; 4],
}

fn room_hallway_pos(room: usize) -> usize {
    room * 2 + 2
}

fn hallway_room_pos(hallway: usize) -> Option<usize> {
    match hallway {
        2 => Some(0),
        4 => Some(1),
        6 => Some(2),
        8 => Some(3),
        _ => None,
    }
}

impl State {
    const VALID_HALLWAY_POS: [usize; 7] = [0, 1, 3, 5, 7, 9, 10];

    pub fn is_done(&self) -> bool {
        self == &State {
            hallway: Default::default(),
            rooms: [
                [Some(Pod::A); 2],
                [Some(Pod::B); 2],
                [Some(Pod::C); 2],
                [Some(Pod::D); 2],
            ],
        }
    }

    fn add_to_queue(self, cost: u32, todo: &mut Todo, visited: &mut Visited) {
        let entry = visited.entry(self.clone());

        if matches!(&entry, Entry::Occupied(entry) if *entry.get() <= cost) {
            // Already got a better one
            return;
        }

        // print!("Next: \n{}", self);

        // nightly only :'(
        // entry.insert(cost);
        *entry.or_default() = cost;

        todo.push(Reverse((cost + self.estimate(), cost, self)))
    }

    fn estimate(&self) -> u32 {
        // A* estimate. For every entry that is not already "at rest", the cost is the cost
        // required to get it to the top of its intended room.

        // Cost to enter the hole for all pods that still need to
        let enter_estimate: u32 = self
            .rooms
            .iter()
            .enumerate()
            .map(|(index, room)| {
                let pod = match index {
                    0 => Pod::A,
                    1 => Pod::B,
                    2 => Pod::C,
                    3 => Pod::D,
                    _ => unreachable!(),
                };

                room.iter()
                    .enumerate()
                    .filter_map(|(index, &entry)| {
                        if Some(pod) != entry {
                            Some(index as u32 + 1)
                        } else {
                            None
                        }
                    })
                    .sum::<u32>()
                    * pod.cost()
            })
            .sum();

        // Cost for all of the hallway to move to above their intended rooms
        let hallway_estimate: u32 = self
            .hallway
            .iter()
            .enumerate()
            .filter_map(|(pos, &pod)| {
                let pod = pod?;

                let destination_pos = room_hallway_pos(pod as usize);

                let (a, b) = ordered(pos, destination_pos);

                Some((b - a) as u32 * pod.cost())
            })
            .sum();

        // Cost to move out of the room and above the correct rooms
        let rooms_estimate: u32 = self
            .rooms
            .iter()
            .enumerate()
            .map(|(room_index, room)| {
                let hallway_pos = room_hallway_pos(room_index);

                room.iter()
                    .enumerate()
                    .filter_map(|(room_pos, &pod)| {
                        let pod = pod?;

                        if pod as usize == room_index {
                            return None;
                        }

                        let destination_pos = room_hallway_pos(pod as usize);
                        let (a, b) = ordered(hallway_pos, destination_pos);

                        let steps = 1 + room_pos + b - a;

                        Some(steps as u32 * pod.cost())
                    })
                    .sum::<u32>()
            })
            .sum();

        enter_estimate + hallway_estimate + rooms_estimate
    }

    pub fn generate_next(&self, cost: u32, todo: &mut Todo, visited: &mut Visited) {
        self.room_to_hallway(cost, todo, visited);
        self.hallway_to_room(cost, todo, visited);
    }

    fn room_to_hallway(&self, cost: u32, todo: &mut Todo, visited: &mut Visited) {
        for (index, room) in self.rooms.iter().enumerate() {
            // Check if we even want to move anything out of this room
            if room
                .iter()
                .all(|entry| entry.map(|pod| pod as usize == index).unwrap_or(true))
            {
                continue;
            }

            let (pos, pod) = room
                .iter()
                .enumerate()
                .find_map(|(pos, entry)| entry.map(|pod| (pos, pod)))
                .unwrap(); // Safe unwrap, we know it exists from above.

            let base_cost = 1 + pos;
            let hallway_pos = room_hallway_pos(index);

            let mut queue_new = |new_pos, new_cost| {
                let mut new_state = self.clone();
                swap(
                    &mut new_state.hallway[new_pos],
                    &mut new_state.rooms[index][pos],
                );

                new_state.add_to_queue(new_cost + cost, todo, visited)
            };

            // Check positions to the left
            for new_pos in (0..hallway_pos).rev() {
                if self.hallway[new_pos].is_some() {
                    // Hit an occupied room
                    break;
                }

                if !Self::VALID_HALLWAY_POS.contains(&new_pos) {
                    // Not allowed to stop here
                    continue;
                }

                let new_cost = (base_cost + hallway_pos - new_pos) as u32 * pod.cost();
                queue_new(new_pos, new_cost);
            }

            // And to the right
            for new_pos in hallway_pos..self.hallway.len() {
                if self.hallway[new_pos].is_some() {
                    // Hit an occupied room
                    break;
                }

                if !Self::VALID_HALLWAY_POS.contains(&new_pos) {
                    // Not allowed to stop here
                    continue;
                }

                let new_cost = (base_cost + new_pos - hallway_pos) as u32 * pod.cost();
                queue_new(new_pos, new_cost);
            }
        }
    }

    fn hallway_to_room(&self, cost: u32, todo: &mut Todo, visited: &mut Visited) {
        for (pos, pod) in self
            .hallway
            .iter()
            .enumerate()
            .filter_map(|(pos, pod)| pod.map(|pod| (pos, pod)))
        {
            let mut enqueue_room = |base_cost, room_index: usize| {
                for (room_pos, entry) in self.rooms[room_index].iter().enumerate() {
                    if entry.is_some() {
                        // Occupied
                        break;
                    }

                    let new_cost = (room_pos + base_cost) as u32 * pod.cost();
                    let mut new_state = self.clone();
                    swap(
                        &mut new_state.hallway[pos],
                        &mut new_state.rooms[room_index][room_pos],
                    );

                    new_state.add_to_queue(new_cost + cost, todo, visited)
                }
            };

            // Try moving into a room to the left
            for new_hallway_pos in (2..pos).rev() {
                if self.hallway[new_hallway_pos].is_some() {
                    // Blocked
                    break;
                }

                let room_index = if let Some(room) = hallway_room_pos(new_hallway_pos) {
                    room
                } else {
                    continue;
                };

                let base_cost = pos - new_hallway_pos + 1;
                enqueue_room(base_cost, room_index);
            }

            // And the right
            for new_hallway_pos in (pos + 1)..=8 {
                if self.hallway[new_hallway_pos].is_some() {
                    // Blocked
                    break;
                }

                let room_index = if let Some(room) = hallway_room_pos(new_hallway_pos) {
                    room
                } else {
                    continue;
                };

                let base_cost = new_hallway_pos - pos + 1;
                enqueue_room(base_cost, room_index);
            }
        }
    }
}

impl Display for State {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let helper = |opt_pod| match opt_pod {
            Some(Pod::A) => 'A',
            Some(Pod::B) => 'B',
            Some(Pod::C) => 'C',
            Some(Pod::D) => 'D',
            None => '.',
        };
        writeln!(f, "#############")?;
        write!(f, "#")?;

        for entry in self.hallway {
            write!(f, "{}", helper(entry))?;
        }
        writeln!(f, "#")?;

        for i in 0..(self.rooms[0].len()) {
            writeln!(
                f,
                "  #{}#{}#{}#{}#",
                helper(self.rooms[0][i]),
                helper(self.rooms[1][i]),
                helper(self.rooms[2][i]),
                helper(self.rooms[3][i])
            )?;
        }

        write!(f, "  #########")
    }
}

fn read_input(input: &mut dyn Read) -> State {
    let mut reader = LineIter::new(input);
    let mut state = State::default();

    let _ = reader.next();
    let _ = reader.next();

    let mut helper = |idx: usize| {
        reader
            .next()
            .unwrap()
            .chars()
            .filter_map(|c| Pod::try_from(c).ok())
            .zip(&mut state.rooms)
            .for_each(|(pod, room)| room[idx] = Some(pod))
    };

    helper(0);
    helper(1);

    state
}

pub fn part1(input: &mut dyn Read) -> String {
    let state = read_input(input);
    let mut todo = Todo::new();

    let mut visited = HashMap::new();
    visited.insert(state.clone(), 0);

    todo.push(Reverse((state.estimate(), 0, state)));

    while let Some(Reverse((_, cost, state))) = todo.pop() {
        if state.is_done() {
            return cost.to_string();
        }

        state.generate_next(cost, &mut todo, &mut visited);
    }

    panic!("No route found!")
}

pub fn part2(_input: &mut dyn Read) -> String {
    todo!()
}

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

    use crate::test_implementation;

    const SAMPLE: &[u8] = include_bytes!("samples/23.txt");

    #[test]
    fn test_is_done() {
        let state = State {
            hallway: Default::default(),
            rooms: [
                [Some(Pod::A); 2],
                [Some(Pod::B), Some(Pod::B)],
                [Some(Pod::C), Some(Pod::C)],
                [Some(Pod::D), Some(Pod::D)],
            ],
        };

        assert!(state.is_done());
    }

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 12521);
    }
}
<commit_msg>Implement part 2<commit_after>use std::cmp::Reverse;
use std::collections::hash_map::Entry;
use std::collections::BinaryHeap;
use std::collections::HashMap;
use std::fmt::Display;
use std::io::Read;
use std::mem::swap;

use crate::common::LineIter;

type Item<const S: usize> = (u32, State<S>);
type Todo<const S: usize> = BinaryHeap<Reverse<Item<S>>>;
type Visited<const S: usize> = HashMap<State<S>, u32>;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
enum Pod {
    A,
    B,
    C,
    D,
}

impl Pod {
    pub fn cost(self) -> u32 {
        match self {
            Pod::A => 1,
            Pod::B => 10,
            Pod::C => 100,
            Pod::D => 1000,
        }
    }
}

impl TryFrom<char> for Pod {
    type Error = &'static str;

    fn try_from(c: char) -> Result<Self, Self::Error> {
        match c {
            'A' => Ok(Pod::A),
            'B' => Ok(Pod::B),
            'C' => Ok(Pod::C),
            'D' => Ok(Pod::D),
            _ => Err("Invalid pod"),
        }
    }
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
struct State<const S: usize> {
    hallway: [Option<Pod>; 11],
    rooms: [[Option<Pod>; S]; 4],
}

fn room_hallway_pos(room: usize) -> usize {
    room * 2 + 2
}

fn abs_delta(a: usize, b: usize) -> usize {
    if a < b {
        b - a
    } else {
        a - b
    }
}

impl<const S: usize> State<S> {
    const VALID_HALLWAY_POS: [usize; 7] = [0, 1, 3, 5, 7, 9, 10];

    pub fn is_done(&self) -> bool {
        self == &State {
            hallway: Default::default(),
            rooms: [
                [Some(Pod::A); S],
                [Some(Pod::B); S],
                [Some(Pod::C); S],
                [Some(Pod::D); S],
            ],
        }
    }

    fn add_to_queue(self, cost: u32, todo: &mut Todo<S>, visited: &mut Visited<S>) {
        let entry = visited.entry(self.clone());

        if matches!(&entry, Entry::Occupied(entry) if *entry.get() <= cost) {
            // Already got a better one
            return;
        }

        // nightly only :'(
        // entry.insert(cost);
        *entry.or_default() = cost;

        todo.push(Reverse((cost + self.estimate(), self)))
    }

    fn estimate(&self) -> u32 {
        // A* estimate. For every entry that is not already "at rest", the cost is the cost
        // required to get it to the top of its intended room.

        // Cost to enter the hole for all pods that still need to
        let enter_estimate: u32 = self
            .rooms
            .iter()
            .enumerate()
            .map(|(index, room)| {
                let pod = match index {
                    0 => Pod::A,
                    1 => Pod::B,
                    2 => Pod::C,
                    3 => Pod::D,
                    _ => unreachable!(),
                };

                room.iter()
                    .enumerate()
                    .rev()
                    .skip_while(|&(_, &entry)| entry == Some(pod))
                    .map(|(index, _)| index as u32 + 1)
                    .sum::<u32>()
                    * pod.cost()
            })
            .sum();

        // Cost for all of the hallway to move to above their intended rooms
        let hallway_estimate: u32 = self
            .hallway
            .iter()
            .enumerate()
            .filter_map(|(pos, &pod)| {
                let pod = pod?;

                let destination_pos = room_hallway_pos(pod as usize);

                Some(abs_delta(pos, destination_pos) as u32 * pod.cost())
            })
            .sum();

        // Cost to move out of the room and above the correct rooms
        let rooms_estimate: u32 = self
            .rooms
            .iter()
            .enumerate()
            .map(|(room_index, room)| {
                let hallway_pos = room_hallway_pos(room_index);

                room.iter()
                    .enumerate()
                    .rev()
                    .skip_while(|&(_, &entry)| {
                        entry.map(|pod| pod as usize == room_index).unwrap_or(false)
                    })
                    .filter_map(|(room_pos, &pod)| {
                        let pod = pod?;

                        let destination_pos = room_hallway_pos(pod as usize);

                        let steps = 1 + room_pos + abs_delta(hallway_pos, destination_pos).max(2);

                        Some(steps as u32 * pod.cost())
                    })
                    .sum::<u32>()
            })
            .sum();

        enter_estimate + hallway_estimate + rooms_estimate
    }

    pub fn generate_next(&self, cost: u32, todo: &mut Todo<S>, visited: &mut Visited<S>) {
        self.room_to_hallway(cost, todo, visited);
        self.hallway_to_room(cost, todo, visited);
    }

    fn room_to_hallway(&self, cost: u32, todo: &mut Todo<S>, visited: &mut Visited<S>) {
        for (index, room) in self.rooms.iter().enumerate() {
            // Check if we even want to move anything out of this room
            if room
                .iter()
                .all(|entry| entry.map(|pod| pod as usize == index).unwrap_or(true))
            {
                continue;
            }

            let (pos, pod) = room
                .iter()
                .enumerate()
                .find_map(|(pos, entry)| entry.map(|pod| (pos, pod)))
                .unwrap(); // Safe unwrap, we know it exists from above.

            let base_cost = 1 + pos;
            let hallway_pos = room_hallway_pos(index);

            let mut queue_new = |new_pos, new_cost| {
                let mut new_state = self.clone();
                swap(
                    &mut new_state.hallway[new_pos],
                    &mut new_state.rooms[index][pos],
                );

                new_state.add_to_queue(new_cost + cost, todo, visited)
            };

            // Check positions to the left
            for new_pos in (0..hallway_pos).rev() {
                if self.hallway[new_pos].is_some() {
                    // Hit an occupied room
                    break;
                }

                if !Self::VALID_HALLWAY_POS.contains(&new_pos) {
                    // Not allowed to stop here
                    continue;
                }

                let new_cost = (base_cost + hallway_pos - new_pos) as u32 * pod.cost();
                queue_new(new_pos, new_cost);
            }

            // And to the right
            for new_pos in hallway_pos..self.hallway.len() {
                if self.hallway[new_pos].is_some() {
                    // Hit an occupied room
                    break;
                }

                if !Self::VALID_HALLWAY_POS.contains(&new_pos) {
                    // Not allowed to stop here
                    continue;
                }

                let new_cost = (base_cost + new_pos - hallway_pos) as u32 * pod.cost();
                queue_new(new_pos, new_cost);
            }
        }
    }

    fn hallway_to_room(&self, cost: u32, todo: &mut Todo<S>, visited: &mut Visited<S>) {
        for (pos, pod) in self
            .hallway
            .iter()
            .enumerate()
            .filter_map(|(pos, pod)| pod.map(|pod| (pos, pod)))
        {
            let room = pod as usize;
            let new_hallway_pos = room_hallway_pos(room);

            // Check if the path is free
            let in_between = if new_hallway_pos < pos {
                &self.hallway[(new_hallway_pos + 1)..pos]
            } else {
                &self.hallway[(pos + 1)..new_hallway_pos]
            };

            if in_between.iter().any(Option::is_some) {
                // Something's in the way
                continue;
            }

            // Check if we can move into the room
            if self.rooms[room]
                .iter()
                .copied()
                .flatten()
                .any(|other| other != pod)
            {
                // Scared of other pods
                continue;
            }

            let room_pos = if let Some(pos) = self.rooms[room].iter().rposition(Option::is_none) {
                pos
            } else {
                continue;
            };

            let new_cost = (abs_delta(pos, new_hallway_pos) + room_pos + 1) as u32 * pod.cost();
            let mut new_state = self.clone();
            swap(
                &mut new_state.hallway[pos],
                &mut new_state.rooms[room][room_pos],
            );
            new_state.add_to_queue(cost + new_cost, todo, visited);
        }
    }

    pub fn solve(&self) -> u32 {
        let mut todo = Todo::new();

        let mut visited = HashMap::new();
        visited.insert(self.clone(), 0);

        todo.push(Reverse((self.estimate(), self.clone())));

        while let Some(Reverse((_, state))) = todo.pop() {
            let cost = *visited.get(&state).unwrap_or(&0);

            if state.is_done() {
                return cost;
            }

            state.generate_next(cost, &mut todo, &mut visited);
        }

        panic!("No route found!")
    }
}

impl<const S: usize> Display for State<S> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let helper = |opt_pod| match opt_pod {
            Some(Pod::A) => 'A',
            Some(Pod::B) => 'B',
            Some(Pod::C) => 'C',
            Some(Pod::D) => 'D',
            None => '.',
        };
        writeln!(f, "#############")?;
        write!(f, "#")?;

        for entry in self.hallway {
            write!(f, "{}", helper(entry))?;
        }
        writeln!(f, "#")?;

        for i in 0..S {
            writeln!(
                f,
                "  #{}#{}#{}#{}#",
                helper(self.rooms[0][i]),
                helper(self.rooms[1][i]),
                helper(self.rooms[2][i]),
                helper(self.rooms[3][i])
            )?;
        }

        write!(f, "  #########")
    }
}

fn read_input(input: &mut dyn Read) -> State<2> {
    let mut reader = LineIter::new(input);
    let mut state = State {
        hallway: Default::default(),
        rooms: Default::default(),
    };

    let _ = reader.next();
    let _ = reader.next();

    let mut helper = |idx: usize| {
        reader
            .next()
            .unwrap()
            .chars()
            .filter_map(|c| Pod::try_from(c).ok())
            .zip(&mut state.rooms)
            .for_each(|(pod, room)| room[idx] = Some(pod))
    };

    helper(0);
    helper(1);

    state
}

pub fn part1(input: &mut dyn Read) -> String {
    let state = read_input(input);

    state.solve().to_string()
}

pub fn part2(input: &mut dyn Read) -> String {
    let state2 = read_input(input);

    let state4 = State {
        hallway: Default::default(),
        rooms: [
            [
                state2.rooms[0][0],
                Some(Pod::D),
                Some(Pod::D),
                state2.rooms[0][1],
            ],
            [
                state2.rooms[1][0],
                Some(Pod::C),
                Some(Pod::B),
                state2.rooms[1][1],
            ],
            [
                state2.rooms[2][0],
                Some(Pod::B),
                Some(Pod::A),
                state2.rooms[2][1],
            ],
            [
                state2.rooms[3][0],
                Some(Pod::A),
                Some(Pod::C),
                state2.rooms[3][1],
            ],
        ],
    };

    state4.solve().to_string()
}

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

    use crate::test_implementation;

    const SAMPLE: &[u8] = include_bytes!("samples/23.txt");

    #[test]
    fn test_is_done() {
        let state = State {
            hallway: Default::default(),
            rooms: [
                [Some(Pod::A); 2],
                [Some(Pod::B); 2],
                [Some(Pod::C); 2],
                [Some(Pod::D); 2],
            ],
        };

        assert!(state.is_done());
    }

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 12521);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 44169);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Configuration options for a single run of the servo application. Created
//! from command line arguments.

use geometry::ScreenPx;

use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use layers::geometry::DevicePixel;
use getopts;
use std::cmp;
use std::io;
use std::mem;
use std::os;
use std::ptr;
use std::rt;

/// Global flags for Servo, currently set on the command line.
#[deriving(Clone)]
pub struct Opts {
    /// The initial URLs to load.
    pub urls: Vec<String>,

    /// How many threads to use for CPU rendering (`-t`).
    ///
    /// FIXME(pcwalton): This is not currently used. All rendering is sequential.
    pub n_render_threads: uint,

    /// True to use GPU painting via Skia-GL, false to use CPU painting via Skia (`-g`). Note that
    /// compositing is always done on the GPU.
    pub gpu_painting: bool,

    /// The maximum size of each tile in pixels (`-s`).
    pub tile_size: uint,

    /// The ratio of device pixels per px at the default scale. If unspecified, will use the
    /// platform default setting.
    pub device_pixels_per_px: Option<ScaleFactor<ScreenPx, DevicePixel, f32>>,

    /// `None` to disable the time profiler or `Some` with an interval in seconds to enable it and
    /// cause it to produce output on that interval (`-p`).
    pub time_profiler_period: Option<f64>,

    /// `None` to disable the memory profiler or `Some` with an interval in seconds to enable it
    /// and cause it to produce output on that interval (`-m`).
    pub memory_profiler_period: Option<f64>,

    /// Enable experimental web features (`-e`).
    pub enable_experimental: bool,

    /// The number of threads to use for layout (`-y`). Defaults to 1, which results in a recursive
    /// sequential algorithm.
    pub layout_threads: uint,

    pub nonincremental_layout: bool,

    /// True to exit after the page load (`-x`).
    pub exit_after_load: bool,

    pub output_file: Option<String>,
    pub headless: bool,
    pub hard_fail: bool,

    /// True if we should bubble intrinsic widths sequentially (`-b`). If this is true, then
    /// intrinsic widths are computed as a separate pass instead of during flow construction. You
    /// may wish to turn this flag on in order to benchmark style recalculation against other
    /// browser engines.
    pub bubble_inline_sizes_separately: bool,

    /// True if we should show borders on all layers and tiles for
    /// debugging purposes (`--show-debug-borders`).
    pub show_debug_borders: bool,

    /// True if we should show borders on all fragments for debugging purposes (`--show-debug-fragment-borders`).
    pub show_debug_fragment_borders: bool,

    /// If set with --disable-text-aa, disable antialiasing on fonts. This is primarily useful for reftests
    /// where pixel perfect results are required when using fonts such as the Ahem
    /// font for layout tests.
    pub enable_text_antialiasing: bool,

    /// True if each step of layout is traced to an external JSON file
    /// for debugging purposes. Settings this implies sequential layout
    /// and render.
    pub trace_layout: bool,

    /// `None` to disable devtools or `Some` with a port number to start a server to listen to
    /// remote Firefox devtools connections.
    pub devtools_port: Option<u16>,

    /// The initial requested size of the window.
    pub initial_window_size: TypedSize2D<ScreenPx, uint>,

    /// An optional string allowing the user agent to be set for testing.
    pub user_agent: Option<String>,

    /// Dumps the flow tree after a layout.
    pub dump_flow_tree: bool,

    /// Whether to show an error when display list geometry escapes flow overflow regions.
    pub validate_display_list_geometry: bool,
}

fn print_usage(app: &str, opts: &[getopts::OptGroup]) {
    let message = format!("Usage: {} [ options ... ] [URL]\n\twhere options include", app);
    println!("{}", getopts::usage(message.as_slice(), opts));
}

fn args_fail(msg: &str) {
    io::stderr().write_line(msg).unwrap();
    os::set_exit_status(1);
}

// Always use CPU rendering on android.

#[cfg(target_os="android")]
static FORCE_CPU_PAINTING: bool = true;

#[cfg(not(target_os="android"))]
static FORCE_CPU_PAINTING: bool = false;

pub fn from_cmdline_args(args: &[String]) -> bool {
    let app_name = args[0].to_string();
    let args = args.tail();

    let opts = vec!(
        getopts::optflag("c", "cpu", "CPU painting (default)"),
        getopts::optflag("g", "gpu", "GPU painting"),
        getopts::optopt("o", "output", "Output file", "output.png"),
        getopts::optopt("s", "size", "Size of tiles", "512"),
        getopts::optopt("", "device-pixel-ratio", "Device pixels per px", ""),
        getopts::optflag("e", "experimental", "Enable experimental web features"),
        getopts::optopt("t", "threads", "Number of render threads", "1"),
        getopts::optflagopt("p", "profile", "Profiler flag and output interval", "10"),
        getopts::optflagopt("m", "memory-profile", "Memory profiler flag and output interval", "10"),
        getopts::optflag("x", "exit", "Exit after load flag"),
        getopts::optopt("y", "layout-threads", "Number of threads to use for layout", "1"),
        getopts::optflag("i", "nonincremental-layout", "Enable to turn off incremental layout."),
        getopts::optflag("z", "headless", "Headless mode"),
        getopts::optflag("f", "hard-fail", "Exit on task failure instead of displaying about:failure"),
        getopts::optflag("b", "bubble-widths", "Bubble intrinsic widths separately like other engines"),
        getopts::optflag("", "show-debug-borders", "Show debugging borders on layers and tiles."),
        getopts::optflag("", "show-debug-fragment-borders", "Show debugging borders on fragments."),
        getopts::optflag("", "disable-text-aa", "Disable antialiasing for text rendering."),
        getopts::optflag("", "trace-layout", "Write layout trace to external file for debugging."),
        getopts::optflagopt("", "devtools", "Start remote devtools server on port", "6000"),
        getopts::optopt("", "resolution", "Set window resolution.", "800x600"),
        getopts::optopt("u", "user-agent", "Set custom user agent string", "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)"),
        getopts::optflag("", "dump-flow-tree", "Dump the flow (render) tree during each layout."),
        getopts::optflag("", "validate-display-list-geometry", "Display an error when display list geometry escapes overflow region."),
        getopts::optflag("h", "help", "Print this message")
    );

    let opt_match = match getopts::getopts(args, opts.as_slice()) {
        Ok(m) => m,
        Err(f) => {
            args_fail(format!("{}", f).as_slice());
            return false;
        }
    };

    if opt_match.opt_present("h") || opt_match.opt_present("help") {
        print_usage(app_name.as_slice(), opts.as_slice());
        return false;
    };

    let urls = if opt_match.free.is_empty() {
        print_usage(app_name.as_slice(), opts.as_slice());
        args_fail("servo asks that you provide 1 or more URLs");
        return false;
    } else {
        opt_match.free.clone()
    };

    let tile_size: uint = match opt_match.opt_str("s") {
        Some(tile_size_str) => from_str(tile_size_str.as_slice()).unwrap(),
        None => 512,
    };

    let device_pixels_per_px = opt_match.opt_str("device-pixel-ratio").map(|dppx_str|
        ScaleFactor(from_str(dppx_str.as_slice()).unwrap())
    );

    let mut n_render_threads: uint = match opt_match.opt_str("t") {
        Some(n_render_threads_str) => from_str(n_render_threads_str.as_slice()).unwrap(),
        None => 1,      // FIXME: Number of cores.
    };

    // If only the flag is present, default to a 5 second period for both profilers.
    let time_profiler_period = opt_match.opt_default("p", "5").map(|period| {
        from_str(period.as_slice()).unwrap()
    });
    let memory_profiler_period = opt_match.opt_default("m", "5").map(|period| {
        from_str(period.as_slice()).unwrap()
    });

    let gpu_painting = !FORCE_CPU_PAINTING && opt_match.opt_present("g");

    let mut layout_threads: uint = match opt_match.opt_str("y") {
        Some(layout_threads_str) => from_str(layout_threads_str.as_slice()).unwrap(),
        None => cmp::max(rt::default_sched_threads() * 3 / 4, 1),
    };

    let nonincremental_layout = opt_match.opt_present("i");

    let mut bubble_inline_sizes_separately = opt_match.opt_present("b");

    let trace_layout = opt_match.opt_present("trace-layout");
    if trace_layout {
        n_render_threads = 1;
        layout_threads = 1;
        bubble_inline_sizes_separately = true;
    }

    let devtools_port = opt_match.opt_default("devtools", "6000").map(|port| {
        from_str(port.as_slice()).unwrap()
    });

    let initial_window_size = match opt_match.opt_str("resolution") {
        Some(res_string) => {
            let res: Vec<uint> = res_string.as_slice().split('x').map(|r| from_str(r).unwrap()).collect();
            TypedSize2D(res[0], res[1])
        }
        None => {
            TypedSize2D(800, 600)
        }
    };

    let opts = Opts {
        urls: urls,
        n_render_threads: n_render_threads,
        gpu_painting: gpu_painting,
        tile_size: tile_size,
        device_pixels_per_px: device_pixels_per_px,
        time_profiler_period: time_profiler_period,
        memory_profiler_period: memory_profiler_period,
        enable_experimental: opt_match.opt_present("e"),
        layout_threads: layout_threads,
        nonincremental_layout: nonincremental_layout,
        exit_after_load: opt_match.opt_present("x"),
        output_file: opt_match.opt_str("o"),
        headless: opt_match.opt_present("z"),
        hard_fail: opt_match.opt_present("f"),
        bubble_inline_sizes_separately: bubble_inline_sizes_separately,
        show_debug_borders: opt_match.opt_present("show-debug-borders"),
        show_debug_fragment_borders: opt_match.opt_present("show-debug-fragment-borders"),
        enable_text_antialiasing: !opt_match.opt_present("disable-text-aa"),
        trace_layout: trace_layout,
        devtools_port: devtools_port,
        initial_window_size: initial_window_size,
        user_agent: opt_match.opt_str("u"),
        dump_flow_tree: opt_match.opt_present("dump-flow-tree"),
        validate_display_list_geometry: opt_match.opt_present("validate-display-list-geometry"),
    };

    set_opts(opts);
    true
}

static mut EXPERIMENTAL_ENABLED: bool = false;

pub fn set_experimental_enabled(new_value: bool) {
    unsafe {
        EXPERIMENTAL_ENABLED = new_value;
    }
}

pub fn experimental_enabled() -> bool {
    unsafe {
        EXPERIMENTAL_ENABLED
    }
}

// Make Opts available globally. This saves having to clone and pass
// opts everywhere it is used, which gets particularly cumbersome
// when passing through the DOM structures.
static mut OPTIONS: *mut Opts = 0 as *mut Opts;

pub fn set_opts(opts: Opts) {
    unsafe {
        let box_opts = box opts;
        OPTIONS = mem::transmute(box_opts);
    }
}

#[inline]
pub fn get<'a>() -> &'a Opts {
    unsafe {
        assert!(OPTIONS != ptr::null_mut());
        mem::transmute(OPTIONS)
    }
}
<commit_msg>auto merge of #3823 : glennw/servo/default-opts, r=mbrubeck<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Configuration options for a single run of the servo application. Created
//! from command line arguments.

use geometry::ScreenPx;

use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use layers::geometry::DevicePixel;
use getopts;
use std::cmp;
use std::io;
use std::mem;
use std::os;
use std::ptr;
use std::rt;

/// Global flags for Servo, currently set on the command line.
#[deriving(Clone)]
pub struct Opts {
    /// The initial URLs to load.
    pub urls: Vec<String>,

    /// How many threads to use for CPU rendering (`-t`).
    ///
    /// FIXME(pcwalton): This is not currently used. All rendering is sequential.
    pub n_render_threads: uint,

    /// True to use GPU painting via Skia-GL, false to use CPU painting via Skia (`-g`). Note that
    /// compositing is always done on the GPU.
    pub gpu_painting: bool,

    /// The maximum size of each tile in pixels (`-s`).
    pub tile_size: uint,

    /// The ratio of device pixels per px at the default scale. If unspecified, will use the
    /// platform default setting.
    pub device_pixels_per_px: Option<ScaleFactor<ScreenPx, DevicePixel, f32>>,

    /// `None` to disable the time profiler or `Some` with an interval in seconds to enable it and
    /// cause it to produce output on that interval (`-p`).
    pub time_profiler_period: Option<f64>,

    /// `None` to disable the memory profiler or `Some` with an interval in seconds to enable it
    /// and cause it to produce output on that interval (`-m`).
    pub memory_profiler_period: Option<f64>,

    /// Enable experimental web features (`-e`).
    pub enable_experimental: bool,

    /// The number of threads to use for layout (`-y`). Defaults to 1, which results in a recursive
    /// sequential algorithm.
    pub layout_threads: uint,

    pub nonincremental_layout: bool,

    /// True to exit after the page load (`-x`).
    pub exit_after_load: bool,

    pub output_file: Option<String>,
    pub headless: bool,
    pub hard_fail: bool,

    /// True if we should bubble intrinsic widths sequentially (`-b`). If this is true, then
    /// intrinsic widths are computed as a separate pass instead of during flow construction. You
    /// may wish to turn this flag on in order to benchmark style recalculation against other
    /// browser engines.
    pub bubble_inline_sizes_separately: bool,

    /// True if we should show borders on all layers and tiles for
    /// debugging purposes (`--show-debug-borders`).
    pub show_debug_borders: bool,

    /// True if we should show borders on all fragments for debugging purposes (`--show-debug-fragment-borders`).
    pub show_debug_fragment_borders: bool,

    /// If set with --disable-text-aa, disable antialiasing on fonts. This is primarily useful for reftests
    /// where pixel perfect results are required when using fonts such as the Ahem
    /// font for layout tests.
    pub enable_text_antialiasing: bool,

    /// True if each step of layout is traced to an external JSON file
    /// for debugging purposes. Settings this implies sequential layout
    /// and render.
    pub trace_layout: bool,

    /// `None` to disable devtools or `Some` with a port number to start a server to listen to
    /// remote Firefox devtools connections.
    pub devtools_port: Option<u16>,

    /// The initial requested size of the window.
    pub initial_window_size: TypedSize2D<ScreenPx, uint>,

    /// An optional string allowing the user agent to be set for testing.
    pub user_agent: Option<String>,

    /// Dumps the flow tree after a layout.
    pub dump_flow_tree: bool,

    /// Whether to show an error when display list geometry escapes flow overflow regions.
    pub validate_display_list_geometry: bool,
}

fn print_usage(app: &str, opts: &[getopts::OptGroup]) {
    let message = format!("Usage: {} [ options ... ] [URL]\n\twhere options include", app);
    println!("{}", getopts::usage(message.as_slice(), opts));
}

fn args_fail(msg: &str) {
    io::stderr().write_line(msg).unwrap();
    os::set_exit_status(1);
}

// Always use CPU rendering on android.

#[cfg(target_os="android")]
static FORCE_CPU_PAINTING: bool = true;

#[cfg(not(target_os="android"))]
static FORCE_CPU_PAINTING: bool = false;

fn default_opts() -> Opts {
    Opts {
        urls: vec!(),
        n_render_threads: 1,
        gpu_painting: false,
        tile_size: 512,
        device_pixels_per_px: None,
        time_profiler_period: None,
        memory_profiler_period: None,
        enable_experimental: false,
        layout_threads: 1,
        nonincremental_layout: false,
        exit_after_load: false,
        output_file: None,
        headless: true,
        hard_fail: true,
        bubble_inline_sizes_separately: false,
        show_debug_borders: false,
        show_debug_fragment_borders: false,
        enable_text_antialiasing: false,
        trace_layout: false,
        devtools_port: None,
        initial_window_size: TypedSize2D(800, 600),
        user_agent: None,
        dump_flow_tree: false,
        validate_display_list_geometry: false,
    }
}

pub fn from_cmdline_args(args: &[String]) -> bool {
    let app_name = args[0].to_string();
    let args = args.tail();

    let opts = vec!(
        getopts::optflag("c", "cpu", "CPU painting (default)"),
        getopts::optflag("g", "gpu", "GPU painting"),
        getopts::optopt("o", "output", "Output file", "output.png"),
        getopts::optopt("s", "size", "Size of tiles", "512"),
        getopts::optopt("", "device-pixel-ratio", "Device pixels per px", ""),
        getopts::optflag("e", "experimental", "Enable experimental web features"),
        getopts::optopt("t", "threads", "Number of render threads", "1"),
        getopts::optflagopt("p", "profile", "Profiler flag and output interval", "10"),
        getopts::optflagopt("m", "memory-profile", "Memory profiler flag and output interval", "10"),
        getopts::optflag("x", "exit", "Exit after load flag"),
        getopts::optopt("y", "layout-threads", "Number of threads to use for layout", "1"),
        getopts::optflag("i", "nonincremental-layout", "Enable to turn off incremental layout."),
        getopts::optflag("z", "headless", "Headless mode"),
        getopts::optflag("f", "hard-fail", "Exit on task failure instead of displaying about:failure"),
        getopts::optflag("b", "bubble-widths", "Bubble intrinsic widths separately like other engines"),
        getopts::optflag("", "show-debug-borders", "Show debugging borders on layers and tiles."),
        getopts::optflag("", "show-debug-fragment-borders", "Show debugging borders on fragments."),
        getopts::optflag("", "disable-text-aa", "Disable antialiasing for text rendering."),
        getopts::optflag("", "trace-layout", "Write layout trace to external file for debugging."),
        getopts::optflagopt("", "devtools", "Start remote devtools server on port", "6000"),
        getopts::optopt("", "resolution", "Set window resolution.", "800x600"),
        getopts::optopt("u", "user-agent", "Set custom user agent string", "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)"),
        getopts::optflag("", "dump-flow-tree", "Dump the flow (render) tree during each layout."),
        getopts::optflag("", "validate-display-list-geometry", "Display an error when display list geometry escapes overflow region."),
        getopts::optflag("h", "help", "Print this message")
    );

    let opt_match = match getopts::getopts(args, opts.as_slice()) {
        Ok(m) => m,
        Err(f) => {
            args_fail(format!("{}", f).as_slice());
            return false;
        }
    };

    if opt_match.opt_present("h") || opt_match.opt_present("help") {
        print_usage(app_name.as_slice(), opts.as_slice());
        return false;
    };

    let urls = if opt_match.free.is_empty() {
        print_usage(app_name.as_slice(), opts.as_slice());
        args_fail("servo asks that you provide 1 or more URLs");
        return false;
    } else {
        opt_match.free.clone()
    };

    let tile_size: uint = match opt_match.opt_str("s") {
        Some(tile_size_str) => from_str(tile_size_str.as_slice()).unwrap(),
        None => 512,
    };

    let device_pixels_per_px = opt_match.opt_str("device-pixel-ratio").map(|dppx_str|
        ScaleFactor(from_str(dppx_str.as_slice()).unwrap())
    );

    let mut n_render_threads: uint = match opt_match.opt_str("t") {
        Some(n_render_threads_str) => from_str(n_render_threads_str.as_slice()).unwrap(),
        None => 1,      // FIXME: Number of cores.
    };

    // If only the flag is present, default to a 5 second period for both profilers.
    let time_profiler_period = opt_match.opt_default("p", "5").map(|period| {
        from_str(period.as_slice()).unwrap()
    });
    let memory_profiler_period = opt_match.opt_default("m", "5").map(|period| {
        from_str(period.as_slice()).unwrap()
    });

    let gpu_painting = !FORCE_CPU_PAINTING && opt_match.opt_present("g");

    let mut layout_threads: uint = match opt_match.opt_str("y") {
        Some(layout_threads_str) => from_str(layout_threads_str.as_slice()).unwrap(),
        None => cmp::max(rt::default_sched_threads() * 3 / 4, 1),
    };

    let nonincremental_layout = opt_match.opt_present("i");

    let mut bubble_inline_sizes_separately = opt_match.opt_present("b");

    let trace_layout = opt_match.opt_present("trace-layout");
    if trace_layout {
        n_render_threads = 1;
        layout_threads = 1;
        bubble_inline_sizes_separately = true;
    }

    let devtools_port = opt_match.opt_default("devtools", "6000").map(|port| {
        from_str(port.as_slice()).unwrap()
    });

    let initial_window_size = match opt_match.opt_str("resolution") {
        Some(res_string) => {
            let res: Vec<uint> = res_string.as_slice().split('x').map(|r| from_str(r).unwrap()).collect();
            TypedSize2D(res[0], res[1])
        }
        None => {
            TypedSize2D(800, 600)
        }
    };

    let opts = Opts {
        urls: urls,
        n_render_threads: n_render_threads,
        gpu_painting: gpu_painting,
        tile_size: tile_size,
        device_pixels_per_px: device_pixels_per_px,
        time_profiler_period: time_profiler_period,
        memory_profiler_period: memory_profiler_period,
        enable_experimental: opt_match.opt_present("e"),
        layout_threads: layout_threads,
        nonincremental_layout: nonincremental_layout,
        exit_after_load: opt_match.opt_present("x"),
        output_file: opt_match.opt_str("o"),
        headless: opt_match.opt_present("z"),
        hard_fail: opt_match.opt_present("f"),
        bubble_inline_sizes_separately: bubble_inline_sizes_separately,
        show_debug_borders: opt_match.opt_present("show-debug-borders"),
        show_debug_fragment_borders: opt_match.opt_present("show-debug-fragment-borders"),
        enable_text_antialiasing: !opt_match.opt_present("disable-text-aa"),
        trace_layout: trace_layout,
        devtools_port: devtools_port,
        initial_window_size: initial_window_size,
        user_agent: opt_match.opt_str("u"),
        dump_flow_tree: opt_match.opt_present("dump-flow-tree"),
        validate_display_list_geometry: opt_match.opt_present("validate-display-list-geometry"),
    };

    set_opts(opts);
    true
}

static mut EXPERIMENTAL_ENABLED: bool = false;

pub fn set_experimental_enabled(new_value: bool) {
    unsafe {
        EXPERIMENTAL_ENABLED = new_value;
    }
}

pub fn experimental_enabled() -> bool {
    unsafe {
        EXPERIMENTAL_ENABLED
    }
}

// Make Opts available globally. This saves having to clone and pass
// opts everywhere it is used, which gets particularly cumbersome
// when passing through the DOM structures.
static mut OPTIONS: *mut Opts = 0 as *mut Opts;

pub fn set_opts(opts: Opts) {
    unsafe {
        let box_opts = box opts;
        OPTIONS = mem::transmute(box_opts);
    }
}

#[inline]
pub fn get<'a>() -> &'a Opts {
    unsafe {
        // If code attempts to retrieve the options and they haven't
        // been set by the platform init code, just return a default
        // set of options. This is mostly useful for unit tests that
        // run through a code path which queries the cmd line options.
        if OPTIONS == ptr::null_mut() {
            set_opts(default_opts());
        }
        mem::transmute(OPTIONS)
    }
}
<|endoftext|>


<commit_before>pub mod ast;
pub mod types;

use self::ast::*;
use self::types::*;
use lexer::types::Token;

pub fn parse(input: Vec<Token>) -> Result<Program> {
    let p = Parser::new(input);
    // FIXME
    Ok(Program(Vec::default()))
}
<commit_msg>Add some tests for parser module<commit_after>pub mod ast;
pub mod types;

use self::ast::*;
use self::types::*;
use lexer::types::Token;

pub fn parse(input: Vec<Token>) -> Result<Program> {
    let p = Parser::new(input);
    // FIXME
    Ok(Program(Vec::default()))
}

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

    fn program_eq(code: &str, stmts: Vec<Stmt>) {
        let tokens = lexer::tokenize(String::from(code).chars()).unwrap();
        assert_eq!(parse(tokens).unwrap(), Program(stmts));
    }

    #[test]
    fn empty() {
        program_eq("", vec![]);
    }

    static code_let_stmts: &str = "
let x = 5;
let y = 10;
let foobar = 838383;
let boo = true;
";

    #[test]
    fn let_stmt() {
        program_eq(code_let_stmts,
                   vec![Stmt::Let(Ident(String::from("x"), token!(Ident, 2, 5, "x")),
                                  Expr::Lit(Literal::Int(5, token!(IntLiteral, 2, 9, "5")))),
                        Stmt::Let(Ident(String::from("y"), token!(Ident, 3, 5, "y")),
                                  Expr::Lit(Literal::Int(10, token!(IntLiteral, 3, 9, "10")))),
                        Stmt::Let(Ident(String::from("foobar"), token!(Ident, 4, 5, "foobar")),
                                  Expr::Lit(Literal::Int(838383,
                                                         token!(IntLiteral, 3, 14, "838383")))),
                        Stmt::Let(Ident(String::from("boo"), token!(Ident, 5, 5, "boo")),
                                  Expr::Lit(Literal::Bool(true,
                                                          token!(BoolLiteral, 4, 11, "true"))))]);
    }

    static code_return_stmts: &str = "
return 5;
return 10;
return 838383;
return true;
";

    static code_mixed_stmts: &str = "
let x = 5;
return 10;
15;
let y = 20;
return false;
";

    static code_fn1: &str = "
fn() {
  return foobar + barfoo;
}
";

    static code_fn2: &str = "
fn(x, y) {
  return x + y;
}
";

    static code_fn3: &str = "
fn() {
  return fn (x, y, z, zz) { return x > y; };
}
";

    static code_call: &str = "
add(2, 3);
add(a, b, 1, 2 * 3, other(4 + 5), add(6, 7 * 8));
fn(a, b) { return a + b; }(1, 2);
";
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! NetBSD-specific raw type definitions

#![stable(feature = "raw_ext", since = "1.1.0")]
#![rustc_deprecated(since = "1.8.0",
                    reason = "these type aliases are no longer supported by \
                              the standard library, the `libc` crate on \
                              crates.io should be used instead for the correct \
                              definitions")]
#![allow(deprecated)]

use os::raw::c_long;
use os::unix::raw::{uid_t, gid_t};

#[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type fflags_t = u32;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;

#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;

#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")]
pub struct stat {
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_dev: u64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_mode: u32,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_ino: u64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_nlink: u32,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_uid: u32,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_gid: u32,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_rdev: u64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_atime: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_atime_nsec: c_long,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_mtime: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_mtime_nsec: c_long,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_ctime: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_ctime_nsec: c_long,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_birthtime: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_birthtime_nsec: c_long,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_size: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_blocks: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_blksize: i32,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_flags: u32,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_gen: u32,
    st_spare: [u32; 2],
}
<commit_msg>Rollup merge of #31737 - dhuseby:netbsd_fixes, r=alexcrichton<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! NetBSD-specific raw type definitions

#![stable(feature = "raw_ext", since = "1.1.0")]
#![rustc_deprecated(since = "1.8.0",
                    reason = "these type aliases are no longer supported by \
                              the standard library, the `libc` crate on \
                              crates.io should be used instead for the correct \
                              definitions")]
#![allow(deprecated)]

use os::raw::c_long;
use os::unix::raw::{uid_t, gid_t};

#[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type fflags_t = u32;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;

#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;

#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")]
pub struct stat {
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_dev: u64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_mode: u32,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_ino: u64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_nlink: u32,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_uid: uid_t,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_gid: gid_t,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_rdev: u64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_atime: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_atime_nsec: c_long,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_mtime: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_mtime_nsec: c_long,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_ctime: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_ctime_nsec: c_long,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_birthtime: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_birthtime_nsec: c_long,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_size: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_blocks: i64,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_blksize: i32,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_flags: u32,
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub st_gen: u32,
    st_spare: [u32; 2],
}
<|endoftext|>


<commit_before>use crate::any::Any;
use crate::ffi::CStr;
use crate::ffi::OsString;
use crate::marker::PhantomData;
use crate::os::wasi::ffi::OsStringExt;
use crate::ptr;
use crate::vec;

static mut ARGC: isize = 0;
static mut ARGV: *const *const u8 = ptr::null();

#[cfg(not(target_feature = "atomics"))]
pub unsafe fn args_lock() -> impl Any {
    // No need for a lock if we're single-threaded, but this function will need
    // to get implemented for multi-threaded scenarios
}

pub unsafe fn init(argc: isize, argv: *const *const u8) {
    let _guard = args_lock();
    ARGC = argc;
    ARGV = argv;
}

pub unsafe fn cleanup() {
    let _guard = args_lock();
    ARGC = 0;
    ARGV = ptr::null();
}

pub struct Args {
    iter: vec::IntoIter<OsString>,
    _dont_send_or_sync_me: PhantomData<*mut ()>,
}

/// Returns the command line arguments
pub fn args() -> Args {
    unsafe {
        let _guard = args_lock();
        let args = (0..ARGC)
            .map(|i| {
                let cstr = CStr::from_ptr(*ARGV.offset(i) as *const libc::c_char);
                OsStringExt::from_vec(cstr.to_bytes().to_vec())
            })
            .collect::<Vec<_>>();
        Args {
            iter: args.into_iter(),
            _dont_send_or_sync_me: PhantomData,
        }
    }
}

impl Args {
    pub fn inner_debug(&self) -> &[OsString] {
        self.iter.as_slice()
    }
}

impl Iterator for Args {
    type Item = OsString;
    fn next(&mut self) -> Option<OsString> {
        self.iter.next()
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

impl ExactSizeIterator for Args {
    fn len(&self) -> usize {
        self.iter.len()
    }
}

impl DoubleEndedIterator for Args {
    fn next_back(&mut self) -> Option<OsString> {
        self.iter.next_back()
    }
}
<commit_msg>wasi: Load arguments via syscalls<commit_after>use crate::ffi::CStr;
use crate::io;
use crate::sys::cvt_wasi;
use crate::ffi::OsString;
use crate::marker::PhantomData;
use crate::os::wasi::ffi::OsStringExt;
use crate::vec;

pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
}

pub unsafe fn cleanup() {
}

pub struct Args {
    iter: vec::IntoIter<OsString>,
    _dont_send_or_sync_me: PhantomData<*mut ()>,
}

/// Returns the command line arguments
pub fn args() -> Args {
    maybe_args().unwrap_or_else(|_| {
        Args {
            iter: Vec::new().into_iter(),
            _dont_send_or_sync_me: PhantomData
        }
    })
}

fn maybe_args() -> io::Result<Args> {
    unsafe {
        let (mut argc, mut argv_buf_size) = (0, 0);
        cvt_wasi(libc::__wasi_args_sizes_get(&mut argc, &mut argv_buf_size))?;

        let mut argc = vec![0 as *mut libc::c_char; argc];
        let mut argv_buf = vec![0; argv_buf_size];
        cvt_wasi(libc::__wasi_args_get(argc.as_mut_ptr(), argv_buf.as_mut_ptr()))?;

        let args = argc.into_iter()
            .map(|ptr| CStr::from_ptr(ptr).to_bytes().to_vec())
            .map(|bytes| OsString::from_vec(bytes))
            .collect::<Vec<_>>();
        Ok(Args {
            iter: args.into_iter(),
            _dont_send_or_sync_me: PhantomData,
        })
    }
}

impl Args {
    pub fn inner_debug(&self) -> &[OsString] {
        self.iter.as_slice()
    }
}

impl Iterator for Args {
    type Item = OsString;
    fn next(&mut self) -> Option<OsString> {
        self.iter.next()
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

impl ExactSizeIterator for Args {
    fn len(&self) -> usize {
        self.iter.len()
    }
}

impl DoubleEndedIterator for Args {
    fn next_back(&mut self) -> Option<OsString> {
        self.iter.next_back()
    }
}
<|endoftext|>


<commit_before>extern crate cli_test_dir;

use cli_test_dir::TestDir;
use std::str::from_utf8;

#[test]
fn show_help() {
    let testdir = TestDir::new("substudy", "show_help");
    let output = testdir.cmd()
        .arg("--help")
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    assert!(from_utf8(&output.stdout).unwrap().find("substudy --help").is_some());
}

#[test]
fn show_version() {
    let testdir = TestDir::new("substudy", "show_version");
    let output = testdir.cmd()
        .arg("--version")
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    assert!(from_utf8(&output.stdout).unwrap().find("substudy ").is_some());
}

#[test]
fn cmd_clean() {
    let testdir = TestDir::new("substudy", "cmd_clean");
    let output = testdir.cmd()
        .arg("clean")
        .arg(testdir.src_path("fixtures/sample.en.srt"))
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    assert!(from_utf8(&output.stdout).unwrap().find("Yay!").is_some());
}

#[test]
fn cmd_combine() {
    let testdir = TestDir::new("substudy", "cmd_combine");
    let output = testdir.cmd()
        .arg("combine")
        .arg(testdir.src_path("fixtures/sample.es.srt"))
        .arg(testdir.src_path("fixtures/sample.en.srt"))
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    assert!(from_utf8(&output.stdout).unwrap().find("Yay!").is_some());
    assert!(from_utf8(&output.stdout).unwrap().find("¡Si!").is_some());
}

#[test]
fn cmd_export_csv() {
    let testdir = TestDir::new("substudy", "cmd_export_csv");
    let output = testdir.cmd()
        .args(&["export", "csv"])
        .arg(testdir.src_path("fixtures/empty.mp4"))
        .arg(testdir.src_path("fixtures/sample.es.srt"))
        .arg(testdir.src_path("fixtures/sample.en.srt"))
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    testdir.expect_path("empty_csv/cards.csv");
    testdir.expect_path("empty_csv/empty_00063_496.jpg");
    testdir.expect_path("empty_csv/empty_00060_828-00066_164.es.mp3");
}
<commit_msg>Test other CLI entry points<commit_after>//! Integration tests for our command-line interface.  We actually run the
//! binary and make sure it produces the expected output.

extern crate cli_test_dir;

use cli_test_dir::TestDir;
use std::str::from_utf8;

#[test]
fn show_help() {
    let testdir = TestDir::new("substudy", "show_help");
    let output = testdir.cmd()
        .arg("--help")
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    assert!(from_utf8(&output.stdout).unwrap().find("substudy --help").is_some());
}

#[test]
fn show_version() {
    let testdir = TestDir::new("substudy", "show_version");
    let output = testdir.cmd()
        .arg("--version")
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    assert!(from_utf8(&output.stdout).unwrap().find("substudy ").is_some());
}

#[test]
fn cmd_clean() {
    let testdir = TestDir::new("substudy", "cmd_clean");
    let output = testdir.cmd()
        .arg("clean")
        .arg(testdir.src_path("fixtures/sample.en.srt"))
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    assert!(from_utf8(&output.stdout).unwrap().find("Yay!").is_some());
}

#[test]
fn cmd_combine() {
    let testdir = TestDir::new("substudy", "cmd_combine");
    let output = testdir.cmd()
        .arg("combine")
        .arg(testdir.src_path("fixtures/sample.es.srt"))
        .arg(testdir.src_path("fixtures/sample.en.srt"))
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    assert!(from_utf8(&output.stdout).unwrap().find("Yay!").is_some());
    assert!(from_utf8(&output.stdout).unwrap().find("¡Si!").is_some());
}

#[test]
fn cmd_export_csv() {
    let testdir = TestDir::new("substudy", "cmd_export_csv");
    let output = testdir.cmd()
        .args(&["export", "csv"])
        .arg(testdir.src_path("fixtures/empty.mp4"))
        .arg(testdir.src_path("fixtures/sample.es.srt"))
        .arg(testdir.src_path("fixtures/sample.en.srt"))
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    testdir.expect_path("empty_csv/cards.csv");
    testdir.expect_path("empty_csv/empty_00063_496.jpg");
    testdir.expect_path("empty_csv/empty_00060_828-00066_164.es.mp3");
}

#[test]
fn cmd_export_review() {
    let testdir = TestDir::new("substudy", "cmd_export_review");
    let output = testdir.cmd()
        .args(&["export", "review"])
        .arg(testdir.src_path("fixtures/empty.mp4"))
        .arg(testdir.src_path("fixtures/sample.es.srt"))
        .arg(testdir.src_path("fixtures/sample.en.srt"))
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    testdir.expect_path("empty_review/index.html");
    testdir.expect_path("empty_review/empty_00063_496.jpg");
    testdir.expect_path("empty_review/empty_00061_828-00065_164.es.mp3");
}

#[test]
fn cmd_export_tracks() {
    let testdir = TestDir::new("substudy", "cmd_export_tracks");
    let output = testdir.cmd()
        .args(&["export", "tracks"])
        .arg(testdir.src_path("fixtures/empty.mp4"))
        .arg(testdir.src_path("fixtures/sample.es.srt"))
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    testdir.expect_path("empty_tracks/playlist.m3u8");
    testdir.expect_path("empty_tracks/empty_00059_828-00067_164.es.mp3");
}

#[test]
fn cmd_list_tracks() {
    let testdir = TestDir::new("substudy", "cmd_export_tracks");
    let output = testdir.cmd()
        .args(&["list", "tracks"])
        .arg(testdir.src_path("fixtures/empty.mp4"))
        .output()
        .expect("could not run substudy");
    assert!(output.status.success());
    assert!(from_utf8(&output.stdout).unwrap().find("#1 es Audio").is_some());
}
<|endoftext|>


<commit_before>use package_manager;
use package::Package;
use error::Error;

use std::process::Command;

#[allow(dead_code)]
#[derive(Clone)]
pub struct Dpkg { a: u16 } // remove dummy field once braced_empty_structs feature is in stable

impl Dpkg {
    pub fn new() -> Dpkg {
        Dpkg { a: 0 }
    }
}

impl package_manager::PackageManager for Dpkg {
    fn installed_packages(&self) -> Result<Vec<Package>, Error> {
        Command::new("dpkg-query").arg("-f").arg("'${Package} ${Version}\n'").arg("-W")
            .output()
            .map_err(|e| Error::PackageError(format!("Error fetching packages: {}", e)))
            .and_then(|c| {
                String::from_utf8(c.stdout)
                    .map_err(|e| Error::ParseError(format!("Error parsing package: {}", e)))
                    .map(|s| s.lines().map(|n| String::from(n)).collect::<Vec<String>>())
            })
            .and_then(|lines| {
                lines.iter()
                    .map(|line| parse_package(line))
                    .collect::<Result<Vec<Package>, _>>()
            })
    }
}

fn parse_package(line: &str) -> Result<Package, Error> {
    match line.splitn(2, ' ').collect::<Vec<_>>() {
        ref parts if parts.len() == 2 => Ok(Package { name: String::from(parts[0]),
                                                      version: String::from(parts[1]) }),
        _ => Err(Error::ParseError(format!("Couldn't parse package: {}", line)))
    }
}

#[test]
fn test_parses_normal_package() {
    assert_eq!(Ok(Package { name: "uuid-runtime".to_string(), version: "2.20.1-5.1ubuntu20.7".to_string() }),
               parse_package("uuid-runtime 2.20.1-5.1ubuntu20.7"));
}

#[test]
fn test_separates_name_and_version_correctly() {
    assert_eq!(Ok(Package { name: "vim".to_string(), version: "2.1 foobar".to_string() }),
               parse_package("vim 2.1 foobar"));
}

#[test]
fn test_rejects_bogus_input() {
    assert_eq!(Err(Error::ParseError("Couldn't parse package: foobar".to_string())),
               parse_package("foobar"))
}
<commit_msg>Put package tests inside tests module.<commit_after>use package_manager;
use package::Package;
use error::Error;

use std::process::Command;

#[allow(dead_code)]
#[derive(Clone)]
pub struct Dpkg { a: u16 } // remove dummy field once braced_empty_structs feature is in stable

impl Dpkg {
    pub fn new() -> Dpkg {
        Dpkg { a: 0 }
    }
}

impl package_manager::PackageManager for Dpkg {
    fn installed_packages(&self) -> Result<Vec<Package>, Error> {
        Command::new("dpkg-query").arg("-f").arg("'${Package} ${Version}\n'").arg("-W")
            .output()
            .map_err(|e| Error::PackageError(format!("Error fetching packages: {}", e)))
            .and_then(|c| {
                String::from_utf8(c.stdout)
                    .map_err(|e| Error::ParseError(format!("Error parsing package: {}", e)))
                    .map(|s| s.lines().map(|n| String::from(n)).collect::<Vec<String>>())
            })
            .and_then(|lines| {
                lines.iter()
                    .map(|line| parse_package(line))
                    .collect::<Result<Vec<Package>, _>>()
            })
    }
}

pub fn parse_package(line: &str) -> Result<Package, Error> {
    match line.splitn(2, ' ').collect::<Vec<_>>() {
        ref parts if parts.len() == 2 => Ok(Package { name: String::from(parts[0]),
                                                      version: String::from(parts[1]) }),
        _ => Err(Error::ParseError(format!("Couldn't parse package: {}", line)))
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use error::Error;
    use package::Package;

    #[test]
    fn test_parses_normal_package() {
        assert_eq!(parse_package("uuid-runtime 2.20.1-5.1ubuntu20.7"),
                   Ok(Package {
                       name: "uuid-runtime".to_string(),
                       version: "2.20.1-5.1ubuntu20.7".to_string() }));
    }

    #[test]
    fn test_separates_name_and_version_correctly() {
        assert_eq!(parse_package("vim 2.1 foobar"),
                   Ok(Package {
                       name: "vim".to_string(),
                       version: "2.1 foobar".to_string() }));
    }

    #[test]
    fn test_rejects_bogus_input() {
        assert_eq!(parse_package("foobar"),
                   Err(Error::ParseError("Couldn't parse package: foobar".to_string())));
    }

}
<|endoftext|>


<commit_before>// Implementing cooperative processes before turning on preemption

#![allow(unused_imports, raw_pointer_derive)]
use core::marker::{Sized, Sync};
use alloc::boxed;
use alloc::boxed::{into_raw, Box};
use super::data_structures::{Queue};
use super::concurrency::{Atomic32};

pub use self::init::{Init};

pub mod init;

// enum for global states
use self::Static::*;

#[allow(dead_code)]
enum Static<T> {
    There(T),
    Null,
}

// constants
const STACK_SIZE: usize = 2048;

// The currently running process
// access through process::current() and process::set_current()
#[allow(dead_code)]
static mut current_proc: Static<*mut Process> = Null;

// Ready q
static mut ready_queue: Static<*mut Queue<Box<Process>>> = Null;

// Next process id
static NEXT_ID: Atomic32 = Atomic32 {i: 0};

// This is the API that all processes must implement
// note: if methods does not take &self, then Process is not object-safe
pub trait Process : Sync {
    fn new(name: &'static str) -> Self
        where Self : Sized;
    fn run(&self) -> usize;
}

// Possible states of process
#[allow(dead_code)]
enum State {
    READY,
    RUNNING,
    BLOCKED,
    TERMINATED,
}

// A wrapper around stack pointers for use in initializing the stack
#[derive(Copy,Clone)]
struct StackPtr {
    ptr: *mut usize,
}

unsafe impl Sync for StackPtr { }

impl StackPtr {
    fn get_stack() -> StackPtr {
        // TODO: fudge
        // Allocate a stack
        let stack = Box::new([0; STACK_SIZE]);

        // set the pointer
        StackPtr {ptr: unsafe {boxed::into_raw(stack) as *mut usize} }
    }

    fn smash(&self) {
        // Get entry point of process
        let entry = start_proc as usize;

        // Smash the stack
        unsafe {
            for idx in (STACK_SIZE - 6)..STACK_SIZE {
                *self.ptr.offset(idx as isize) = 0;
            }
            *self.ptr.offset(-2) = entry;
        }
    }

    fn get_kesp(&self) -> StackPtr {
        // Create a new struct that contains the starting
        // kesp value for this stack
        StackPtr {ptr : (self.ptr as usize - 6) as *mut usize}
    }
}

// The entry point of all processes
fn start_proc() {
    // get current process
    (*current()).run();
}

// This function initializes the process infrastructure
pub fn init() {
    printf!("In process init\n");

    // create the ready queue
    unsafe {
        ready_queue = There(boxed::into_raw(Box::new(Queue::new())));
    }

    // create Init process, but do not start it
    let init: Box<Process> = Box::new(Init::new("Init"));

    // add to ready queue
    make_ready(init);

    printf!("Created init process\n");
}

// get the current process as a Box
#[allow(dead_code)]
pub fn current() -> Box<Process> {
    // TODO: lock here
    unsafe {
        match current_proc {
            There(p) => {
                Box::from_raw(p)
            }
            Null   => {
                panic!("No current process");
            }
        }
    }
    // TODO: lock here
}

// set the current process from a Box
#[allow(dead_code)]
pub fn set_current(process: Box<Process>) {

    // TODO: lock here

    unsafe {
        current_proc = There(boxed::into_raw(process));
    }

    // TODO: unlock here
}

// Yield to the next process waiting
pub fn proc_yield() {
    //TODO
}

// Add the process to the Ready queue
pub fn make_ready(process: Box<Process>) {

    // TODO: lock here

    unsafe {
        match ready_queue {
            There(q) => {
                let mut rq: Box<Queue<Box<Process>>> = Box::from_raw(q);
                (*rq).push(process);
            }
            Null => {
                panic!("Unitialized ready queue");
            }
        }
    }

    // TODO: unlock here
}
<commit_msg>Minor clean up and comments<commit_after>// Implementing cooperative processes before turning on preemption

#![allow(unused_imports, raw_pointer_derive)]
use core::marker::{Sized, Sync};
use alloc::boxed;
use alloc::boxed::{into_raw, Box};
use super::data_structures::{Queue};
use super::concurrency::{Atomic32};

pub use self::init::{Init};
use self::Static::*;

pub mod init;

// enum for global states
#[allow(dead_code)]
enum Static<T> {
    There(T),
    Null,
}

// constants
const STACK_SIZE: usize = 2048;

// The currently running process
// access through process::current() and process::set_current()
#[allow(dead_code)]
static mut current_proc: Static<*mut Process> = Null;

// Ready q
// add to the queue with make_ready()
static mut ready_queue: Static<*mut Queue<Box<Process>>> = Null;

// Next process id
// use NEXT_ID.get_then_add()
static NEXT_ID: Atomic32 = Atomic32 {i: 0};

// This is the API that all processes must implement
// note: if methods does not take &self, then Process is not object-safe
pub trait Process : Sync {
    fn new(name: &'static str) -> Self
        where Self : Sized;
    fn run(&self) -> usize;
}

// Possible states of process
#[allow(dead_code)]
enum State {
    READY,
    RUNNING,
    BLOCKED,
    TERMINATED,
}

// A wrapper around stack pointers for use in initializing the stack
#[derive(Copy,Clone)]
struct StackPtr {
    ptr: *mut usize,
}

unsafe impl Sync for StackPtr { }

impl StackPtr {
    fn get_stack() -> StackPtr {
        // TODO: fudge
        // Allocate a stack
        let stack = Box::new([0; STACK_SIZE]);

        // set the pointer
        StackPtr {ptr: unsafe {boxed::into_raw(stack) as *mut usize} }
    }

    fn smash(&self) {
        // Get entry point of process
        let entry = start_proc as usize;

        // Smash the stack
        unsafe {
            for idx in (STACK_SIZE - 6)..STACK_SIZE {
                *self.ptr.offset(idx as isize) = 0;
            }
            *self.ptr.offset(-2) = entry;
        }
    }

    fn get_kesp(&self) -> StackPtr {
        // Create a new struct that contains the starting
        // kesp value for this stack
        StackPtr {ptr : (self.ptr as usize - 6) as *mut usize}
    }
}

// The entry point of all processes
fn start_proc() {
    // get current process
    (*current()).run();
}

// This function initializes the process infrastructure
pub fn init() {
    printf!("In process init\n");

    // create the ready queue
    unsafe {
        ready_queue = There(boxed::into_raw(Box::new(Queue::new())));
    }

    // create Init process, but do not start it
    let init: Box<Process> = Box::new(Init::new("Init"));

    // add to ready queue
    make_ready(init);

    printf!("Created init process\n");
}

// get the current process as a Box
#[allow(dead_code)]
pub fn current() -> Box<Process> {
    // TODO: lock here
    unsafe {
        match current_proc {
            There(p) => {
                Box::from_raw(p)
            }
            Null   => {
                panic!("No current process");
            }
        }
    }
    // TODO: lock here
}

// set the current process from a Box
#[allow(dead_code)]
pub fn set_current(process: Box<Process>) {

    // TODO: lock here

    unsafe {
        current_proc = There(boxed::into_raw(process));
    }

    // TODO: unlock here
}

// Yield to the next process waiting
pub fn proc_yield() {
    //TODO
}

// Add the process to the Ready queue
pub fn make_ready(process: Box<Process>) {

    // TODO: lock here

    unsafe {
        match ready_queue {
            There(q) => {
                let mut rq: Box<Queue<Box<Process>>> = Box::from_raw(q);
                (*rq).push(process);
            }
            Null => {
                panic!("Unitialized ready queue");
            }
        }
    }

    // TODO: unlock here
}
<|endoftext|>


<commit_before>// Queries GitHub and backfills organization IDs for all teams without one
//
// Usage:
//      cargo run --bin backfill-org-ids

#![warn(clippy::all, rust_2018_idioms)]

use cargo_registry::{db, models::Team, schema::teams};

use diesel::prelude::*;
use regex::Regex;
use reqwest::blocking::{Client, Response};
use serde::Deserialize;
use std::env;

fn main() {
    let conn = db::connect_now().unwrap();
    let client = Client::new();
    conn.transaction::<_, diesel::result::Error, _>(|| {
        backfill_org_ids(&conn, &client);
        Ok(())
    })
    .unwrap()
}

fn backfill_org_ids(conn: &PgConnection, client: &Client) {
    let teams = teams::table
        .filter(teams::org_id.is_null())
        .load::<Team>(conn)
        .unwrap();

    let mut found = 0;
    for team in &teams {
        let option_org_id = query_for_org_id(client, team);
        match option_org_id {
            Some(org_id) => {
                found += 1;
                let update_result = diesel::update(teams::table)
                    .filter(teams::github_id.eq(team.github_id))
                    .set(teams::org_id.eq(org_id))
                    .execute(conn);
                if let Err(msg) = update_result {
                    println!(
                        "problem when updating record for team '{}': {}",
                        team.login, msg
                    );
                }
            }
            None => println!("could not find org id for team '{}'", team.login),
        }
    }

    println!(
        "Recorded organization ids for {} of {} teams",
        found,
        teams.len()
    );
}

fn query_for_org_id(client: &Client, team: &Team) -> Option<i32> {
    query_using_team_id(client, team).or_else(|| query_using_org_name(client, team))
}

fn query_using_team_id(client: &Client, team: &Team) -> Option<i32> {
    // GET https://api.github.com/teams/2874034
    let res = github_http_get(
        client,
        &format!("https://api.github.com/teams/{}", team.github_id),
    )?;

    // HTTP/1.1 404 Not Found
    // link: <https://docs.github.com/changes/2020-01-21-moving-the-team-api-endpoints/>; rel="deprecation"; type="text/html", <https://api.github.com/organizations/14631425/team/2874034>; rel="alternate"
    let link = res.headers().get("Link")?.to_str().ok()?;
    let links = parse_link_header::parse(link).ok()?;
    let re = Regex::new(r"/organizations/(\d+)/team").ok()?;
    let re_captures = re.captures(&links.get(&Some("alternate".into()))?.raw_uri)?;
    let org_id_str = re_captures.get(1)?.as_str();
    let org_id = i32::from_str_radix(&org_id_str, 10).ok()?;

    if org_id == 0 {
        None
    } else {
        Some(org_id)
    }
}

fn query_using_org_name(client: &Client, team: &Team) -> Option<i32> {
    let re = Regex::new(r":([^:]+):").unwrap();
    let org_name = re.captures(&team.login)?.get(1)?.as_str();

    // GET https://api.github.com/orgs/rust-lang-nursery
    let res = github_http_get(client, &format!("https://api.github.com/orgs/{}", org_name))?;
    if !res.status().is_success() {
        return None;
    }

    #[derive(Deserialize)]
    struct GithubOrganization {
        id: i32,
    }

    let org = res.json::<GithubOrganization>().ok()?;

    Some(org.id)
}

fn github_http_get(client: &Client, url: &str) -> Option<Response> {
    let gh_client_id = env::var("GH_CLIENT_ID").expect("must set GH_CLIENT_ID");
    let gh_client_secret = env::var("GH_CLIENT_SECRET").expect("must set GH_CLIENT_SECRET");

    let resp_result = client
        .get(url)
        .header(reqwest::header::ACCEPT, "application/vnd.github.v3+json")
        .header(reqwest::header::USER_AGENT, "crates.io (https://crates.io)")
        .basic_auth(&gh_client_id, Some(&gh_client_secret))
        .send();

    resp_result.ok()
}
<commit_msg>No need for a transaction<commit_after>// Queries GitHub and backfills organization IDs for all teams without one
//
// Usage:
//      cargo run --bin backfill-org-ids

#![warn(clippy::all, rust_2018_idioms)]

use cargo_registry::{db, models::Team, schema::teams};

use diesel::prelude::*;
use regex::Regex;
use reqwest::blocking::{Client, Response};
use serde::Deserialize;
use std::env;

fn main() {
    let conn = db::connect_now().unwrap();
    let client = Client::new();
    backfill_org_ids(&conn, &client);
}

fn backfill_org_ids(conn: &PgConnection, client: &Client) {
    let teams = teams::table
        .filter(teams::org_id.is_null())
        .load::<Team>(conn)
        .unwrap();

    let mut found = 0;
    for team in &teams {
        let option_org_id = query_for_org_id(client, team);
        match option_org_id {
            Some(org_id) => {
                found += 1;
                let update_result = diesel::update(teams::table)
                    .filter(teams::github_id.eq(team.github_id))
                    .set(teams::org_id.eq(org_id))
                    .execute(conn);
                if let Err(msg) = update_result {
                    println!(
                        "problem when updating record for team '{}': {}",
                        team.login, msg
                    );
                }
            }
            None => println!("could not find org id for team '{}'", team.login),
        }
    }

    println!(
        "Recorded organization ids for {} of {} teams",
        found,
        teams.len()
    );
}

fn query_for_org_id(client: &Client, team: &Team) -> Option<i32> {
    query_using_team_id(client, team).or_else(|| query_using_org_name(client, team))
}

fn query_using_team_id(client: &Client, team: &Team) -> Option<i32> {
    // GET https://api.github.com/teams/2874034
    let res = github_http_get(
        client,
        &format!("https://api.github.com/teams/{}", team.github_id),
    )?;

    // HTTP/1.1 404 Not Found
    // link: <https://docs.github.com/changes/2020-01-21-moving-the-team-api-endpoints/>; rel="deprecation"; type="text/html", <https://api.github.com/organizations/14631425/team/2874034>; rel="alternate"
    let link = res.headers().get("Link")?.to_str().ok()?;
    let links = parse_link_header::parse(link).ok()?;
    let re = Regex::new(r"/organizations/(\d+)/team").ok()?;
    let re_captures = re.captures(&links.get(&Some("alternate".into()))?.raw_uri)?;
    let org_id_str = re_captures.get(1)?.as_str();
    let org_id = i32::from_str_radix(&org_id_str, 10).ok()?;

    if org_id == 0 {
        None
    } else {
        Some(org_id)
    }
}

fn query_using_org_name(client: &Client, team: &Team) -> Option<i32> {
    let re = Regex::new(r":([^:]+):").unwrap();
    let org_name = re.captures(&team.login)?.get(1)?.as_str();

    // GET https://api.github.com/orgs/rust-lang-nursery
    let res = github_http_get(client, &format!("https://api.github.com/orgs/{}", org_name))?;
    if !res.status().is_success() {
        return None;
    }

    #[derive(Deserialize)]
    struct GithubOrganization {
        id: i32,
    }

    let org = res.json::<GithubOrganization>().ok()?;

    Some(org.id)
}

fn github_http_get(client: &Client, url: &str) -> Option<Response> {
    let gh_client_id = env::var("GH_CLIENT_ID").expect("must set GH_CLIENT_ID");
    let gh_client_secret = env::var("GH_CLIENT_SECRET").expect("must set GH_CLIENT_SECRET");

    let resp_result = client
        .get(url)
        .header(reqwest::header::ACCEPT, "application/vnd.github.v3+json")
        .header(reqwest::header::USER_AGENT, "crates.io (https://crates.io)")
        .basic_auth(&gh_client_id, Some(&gh_client_secret))
        .send();

    resp_result.ok()
}
<|endoftext|>