<commit_before>#[macro_use]
extern crate log;

pub mod acquire;
pub mod analog;
pub mod burst;
pub mod data;
pub mod digital;
pub mod generator;
pub mod socket;
pub mod trigger;

#[derive(Clone)]
pub struct Redpitaya {
    pub acquire: acquire::Acquire,
    pub analog: analog::Analog,
    pub burst: burst::Burst,
    pub digital: digital::Digital,
    pub generator: generator::Generator,
    pub trigger: trigger::Trigger,
    pub data: data::Data,
}

impl Redpitaya {
    pub fn new<S>(addr: S) -> Redpitaya
        where S: ::std::net::ToSocketAddrs
    {
        let socket = socket::Socket::new(addr);

        Redpitaya {
            acquire: acquire::Acquire::new(socket.clone()),
            analog: analog::Analog::new(socket.clone()),
            burst: burst::Burst::new(socket.clone()),
            digital: digital::Digital::new(socket.clone()),
            generator: generator::Generator::new(socket.clone()),
            trigger: trigger::Trigger::new(socket.clone()),
            data: data::Data::new(socket.clone()),
        }
    }
}

#[cfg(test)]
mod test {
    use ::std::io::Read;
    use ::std::io::Write;

    pub fn launch_server() -> (::std::net::SocketAddr, ::std::sync::mpsc::Receiver<String>) {
        let addr = next_test_ip4();
        let listener = ::std::net::TcpListener::bind(format!("{}", addr))
            .unwrap();

        let (tx, rx) = ::std::sync::mpsc::channel();

        ::std::thread::spawn(move || {
            loop {
                if let Ok((mut stream, _)) =  listener.accept() {
                    let tx = tx.clone();

                    ::std::thread::spawn(move || {
                        let mut message = String::new();

                        loop {
                            let mut buffer = [0; 1];

                            stream.read(&mut buffer[..])
                                .unwrap();
                            message.push(buffer[0] as char);

                            if buffer[0] == ('\n' as u8) {
                                break;
                            }
                        }

                        match message.as_str() {
                            "ACQ:DEC?\r\n" => {
                                stream.write("1\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:AVG?\r\n" => {
                                stream.write("ON\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:WPOS?\r\n" => {
                                stream.write("1024\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:TPOS?\r\n" => {
                                stream.write("512\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:SOUR1:DATA:STA:END? 10,13\r\n" => {
                                stream.write("{123,231,-231}\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:SOUR1:DATA:STA:N? 10,3\r\n" => {
                                stream.write("{1.2,3.2,-1.2}\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:SOUR1:DATA?\r\n" => {
                                stream.write("{1.2,3.2,-1.2}\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:SOUR1:DATA:OLD:N? 2\r\n" => {
                                stream.write("{3.2,-1.2}\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:SOUR1:DATA:LAT:N? 2\r\n" => {
                                stream.write("{1.2,3.2}\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:BUF:SIZE?\r\n" => {
                                stream.write("16384\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:TRIG:STAT?\r\n" => {
                                stream.write("WAIT\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:TRIG:DLY?\r\n" => {
                                stream.write("2314\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:TRIG:DLY:NS?\r\n" => {
                                stream.write("128ns\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:TRIG:LEV?\r\n" => {
                                stream.write("123mV\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ANALOG:PIN? AIN1\r\n" => {
                                stream.write("1.34\r\n".as_bytes())
                                    .unwrap();
                            },
                            "DIG:PIN? DIO0_N\r\n" => {
                                stream.write("1\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:DCYC?\r\n" => {
                                stream.write("1.0\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:FREQ:FIX?\r\n" => {
                                stream.write("1000\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR2:FREQ:FIX?\r\n" => {
                                stream.write("8.82604e+06\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:FUNC?\r\n" => {
                                stream.write("SINE\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:STATE?\r\n" => {
                                stream.write("1\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:VOLT?\r\n" => {
                                stream.write("-1.1\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:VOLT:OFFS?\r\n" => {
                                stream.write("1.2\r\n".as_bytes())
                                    .unwrap();
                            },
                            _ => tx.send(message).unwrap(),
                        };
                    });
                }
            }
        });

        (addr, rx)
    }

    static PORT: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;

    fn next_test_ip4() -> ::std::net::SocketAddr {
        let port = PORT.fetch_add(1, ::std::sync::atomic::Ordering::SeqCst) as u16 + base_port();
        ::std::net::SocketAddr::V4(::std::net::SocketAddrV4::new(::std::net::Ipv4Addr::new(127, 0, 0, 1), port))
    }

    // The bots run multiple builds at the same time, and these builds
    // all want to use ports. This function figures out which workspace
    // it is running in and assigns a port range based on it.
    fn base_port() -> u16 {
        let cwd = ::std::env::current_dir()
            .unwrap();
        let dirs = [
            "32-opt",
            "32-nopt",
            "musl-64-opt",
            "cross-opt",
            "64-opt",
            "64-nopt",
            "64-opt-vg",
            "64-debug-opt",
            "all-opt",
            "snap3",
            "dist",
        ];

        dirs.iter()
            .enumerate()
            .find(|&(_, dir)| cwd.to_str().unwrap().contains(dir))
            .map(|p| p.0)
            .unwrap_or(0) as u16 * 1000 + 19600
    }
}
<commit_msg>Implement ::std::default::Default for Redpitaya<commit_after>#[macro_use]
extern crate log;

pub mod acquire;
pub mod analog;
pub mod burst;
pub mod data;
pub mod digital;
pub mod generator;
pub mod socket;
pub mod trigger;

#[derive(Clone)]
pub struct Redpitaya {
    pub acquire: acquire::Acquire,
    pub analog: analog::Analog,
    pub burst: burst::Burst,
    pub digital: digital::Digital,
    pub generator: generator::Generator,
    pub trigger: trigger::Trigger,
    pub data: data::Data,
}

impl Redpitaya {
    pub fn new<S>(addr: S) -> Redpitaya
        where S: ::std::net::ToSocketAddrs
    {
        let socket = socket::Socket::new(addr);

        Redpitaya {
            acquire: acquire::Acquire::new(socket.clone()),
            analog: analog::Analog::new(socket.clone()),
            burst: burst::Burst::new(socket.clone()),
            digital: digital::Digital::new(socket.clone()),
            generator: generator::Generator::new(socket.clone()),
            trigger: trigger::Trigger::new(socket.clone()),
            data: data::Data::new(socket.clone()),
        }
    }
}

impl ::std::default::Default for Redpitaya {
    fn default() -> Self {
        Redpitaya::new("127.0.0.1:5000")
    }
}

#[cfg(test)]
mod test {
    use ::std::io::Read;
    use ::std::io::Write;

    pub fn launch_server() -> (::std::net::SocketAddr, ::std::sync::mpsc::Receiver<String>) {
        let addr = next_test_ip4();
        let listener = ::std::net::TcpListener::bind(format!("{}", addr))
            .unwrap();

        let (tx, rx) = ::std::sync::mpsc::channel();

        ::std::thread::spawn(move || {
            loop {
                if let Ok((mut stream, _)) =  listener.accept() {
                    let tx = tx.clone();

                    ::std::thread::spawn(move || {
                        let mut message = String::new();

                        loop {
                            let mut buffer = [0; 1];

                            stream.read(&mut buffer[..])
                                .unwrap();
                            message.push(buffer[0] as char);

                            if buffer[0] == ('\n' as u8) {
                                break;
                            }
                        }

                        match message.as_str() {
                            "ACQ:DEC?\r\n" => {
                                stream.write("1\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:AVG?\r\n" => {
                                stream.write("ON\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:WPOS?\r\n" => {
                                stream.write("1024\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:TPOS?\r\n" => {
                                stream.write("512\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:SOUR1:DATA:STA:END? 10,13\r\n" => {
                                stream.write("{123,231,-231}\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:SOUR1:DATA:STA:N? 10,3\r\n" => {
                                stream.write("{1.2,3.2,-1.2}\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:SOUR1:DATA?\r\n" => {
                                stream.write("{1.2,3.2,-1.2}\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:SOUR1:DATA:OLD:N? 2\r\n" => {
                                stream.write("{3.2,-1.2}\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:SOUR1:DATA:LAT:N? 2\r\n" => {
                                stream.write("{1.2,3.2}\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:BUF:SIZE?\r\n" => {
                                stream.write("16384\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:TRIG:STAT?\r\n" => {
                                stream.write("WAIT\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:TRIG:DLY?\r\n" => {
                                stream.write("2314\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:TRIG:DLY:NS?\r\n" => {
                                stream.write("128ns\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ACQ:TRIG:LEV?\r\n" => {
                                stream.write("123mV\r\n".as_bytes())
                                    .unwrap();
                            },
                            "ANALOG:PIN? AIN1\r\n" => {
                                stream.write("1.34\r\n".as_bytes())
                                    .unwrap();
                            },
                            "DIG:PIN? DIO0_N\r\n" => {
                                stream.write("1\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:DCYC?\r\n" => {
                                stream.write("1.0\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:FREQ:FIX?\r\n" => {
                                stream.write("1000\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR2:FREQ:FIX?\r\n" => {
                                stream.write("8.82604e+06\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:FUNC?\r\n" => {
                                stream.write("SINE\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:STATE?\r\n" => {
                                stream.write("1\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:VOLT?\r\n" => {
                                stream.write("-1.1\r\n".as_bytes())
                                    .unwrap();
                            },
                            "SOUR1:VOLT:OFFS?\r\n" => {
                                stream.write("1.2\r\n".as_bytes())
                                    .unwrap();
                            },
                            _ => tx.send(message).unwrap(),
                        };
                    });
                }
            }
        });

        (addr, rx)
    }

    static PORT: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;

    fn next_test_ip4() -> ::std::net::SocketAddr {
        let port = PORT.fetch_add(1, ::std::sync::atomic::Ordering::SeqCst) as u16 + base_port();
        ::std::net::SocketAddr::V4(::std::net::SocketAddrV4::new(::std::net::Ipv4Addr::new(127, 0, 0, 1), port))
    }

    // The bots run multiple builds at the same time, and these builds
    // all want to use ports. This function figures out which workspace
    // it is running in and assigns a port range based on it.
    fn base_port() -> u16 {
        let cwd = ::std::env::current_dir()
            .unwrap();
        let dirs = [
            "32-opt",
            "32-nopt",
            "musl-64-opt",
            "cross-opt",
            "64-opt",
            "64-nopt",
            "64-opt-vg",
            "64-debug-opt",
            "all-opt",
            "snap3",
            "dist",
        ];

        dirs.iter()
            .enumerate()
            .find(|&(_, dir)| cwd.to_str().unwrap().contains(dir))
            .map(|p| p.0)
            .unwrap_or(0) as u16 * 1000 + 19600
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;

pub fn puzzle(input: &str) -> u32 {
    let mut floor1 = HashSet::new();
    floor1.insert(Component::Generator(Element::Thulium));
    floor1.insert(Component::Microchip(Element::Thulium));
    floor1.insert(Component::Generator(Element::Plutonium));
    floor1.insert(Component::Generator(Element::Strontium));

    let mut floor2 = HashSet::new();
    floor2.insert(Component::Microchip(Element::Plutonium));
    floor2.insert(Component::Microchip(Element::Strontium));

    let mut floor3 = HashSet::new();
    floor3.insert(Component::Generator(Element::Promethium));
    floor3.insert(Component::Microchip(Element::Promethium));
    floor3.insert(Component::Generator(Element::Ruthenium));
    floor3.insert(Component::Microchip(Element::Ruthenium));

    let initial_building_state = BuildingState {
        elevator_floor: 0,
        floors: [
            floor1,
            floor2,
            floor3,
            HashSet::new(),
        ],
    };
    let initial_world_state = WorldState {
        steps: 0,
        building: initial_building_state.clone()
    };
    let mut queue = vec![initial_world_state];
    let mut seen = vec![initial_building_state];
    let mut steps = 0;

    while !queue.is_empty() {
        let world = queue.remove(0);

        if world.steps != steps {
            println!("{} step(s), queue: {}", world.steps, queue.len());
            steps = world.steps;
        }

        seen.push(world.building.clone());

        if world.steps == 100 {
            panic!("too far!");
        }
        if world.in_end_state() {
            return world.steps;
        }

        let mut valid_next_moves = world.next_moves(&seen);
        queue.append(&mut valid_next_moves);
    }

    panic!("Exhausted all possible moves without finding end condition!");
}

#[derive(Debug)]
pub struct WorldState {
    steps: u32,
    building: BuildingState,
}

impl WorldState {
    pub fn in_end_state(&self) -> bool {
        self.building.in_end_state()
    }

    pub fn next_moves(&self, seen: &Vec<BuildingState>) -> Vec<WorldState> {
        self.building.next_moves(seen).into_iter().map(|b| {
            WorldState {
                steps: self.steps + 1,
                building: b,
            }
        }).collect()
    }
}

#[derive(PartialEq, Eq, Copy, Clone, Debug, PartialOrd, Ord, Hash)]
pub enum Component {
    Microchip(Element),
    Generator(Element),
}

impl Component {
    fn is_fried(&self, others: &HashSet<Component>) -> bool {
        match *self {
            Component::Generator(my_element) => {
                // There's microchip(s) that are not my element
                // and no generator that is that microchip's element
                (*others).iter().any(|&c|
                    match c {
                        Component::Microchip(e) if e != my_element && !(*others).contains(&Component::Generator(e)) => true,
                        _ => false,
                    }
                )
            },
            Component::Microchip(my_element) => {
                // There's some generator that is not my element
                (*others).iter().find(|&c| {
                    match *c {
                        Component::Generator(e) if e != my_element => true,
                        _ => false,
                    }
                }).is_some() &&
                // There is no generator that is my element
                (*others).iter().find(|&c| {
                    match *c {
                        Component::Generator(e) if e == my_element => true,
                        _ => false,
                    }
                }).is_none()
            }
        }
    }
}

#[derive(PartialEq, Eq, Copy, Clone, Debug, PartialOrd, Ord, Hash)]
pub enum Element {
    Hydrogen,
    Lithium,
    Thulium,
    Plutonium,
    Strontium,
    Promethium,
    Ruthenium,
}

#[derive(Debug, PartialEq)]
pub struct BuildingState {
    elevator_floor: usize,
    floors: [HashSet<Component>; 4],
}

// impl PartialEq for BuildingState {
//     fn eq(&self, other: &BuildingState) -> bool {
//         if self.elevator_floor != other.elevator_floor {
//             return false;
//         }
//         for (self_floor, other_floor) in self.floors.iter().zip(other.floors.iter()) {
//             let mut self_floor = self_floor.clone();
//             let mut other_floor = other_floor.clone();
//             self_floor.sort();
//             other_floor.sort();
//             if self_floor != other_floor {
//                 return false;
//             }
//         }
//
//         true
//     }
// }

impl Clone for BuildingState {
    fn clone(&self) -> BuildingState {
        BuildingState {
            elevator_floor: self.elevator_floor,
            floors: [
                self.floors[0].clone(),
                self.floors[1].clone(),
                self.floors[2].clone(),
                self.floors[3].clone(),
            ]
        }
    }
}

impl BuildingState {
    pub fn in_end_state(&self) -> bool {
        self.floors[0].is_empty() &&
            self.floors[1].is_empty() &&
            self.floors[2].is_empty()
    }

    pub fn next_floors(&self) -> Vec<usize> {
        match self.elevator_floor {
            0 => vec![1],
            1 => vec![0, 2],
            2 => vec![1, 3],
            3 => vec![2],
            other => panic!("How did you get on floor {}, elevator?", other),
        }
    }

    pub fn elevator_combos(&self) -> Vec<Vec<Component>> {
        // It sure would be nice to be able to have HashSet<HashSet<Component>>
        // here so that I don't have to filter out duplicates myself.

        let mut result = vec![];
        let mut components = self.floors[self.elevator_floor].iter();
        while let Some(&component) = components.next() {
            // Could take just this item on the elevator
            result.push(vec![component]);
            // Could take this item and one other, if they don't fry
            for &remaining_component in components.clone() {
                let mut hs = HashSet::new();
                hs.insert(remaining_component);
                if !component.is_fried(&hs) {
                    result.push(vec![component, remaining_component]);
                }
            }
        }
        result
    }

    pub fn next_moves(&self, seen: &Vec<BuildingState>) -> Vec<BuildingState> {
        let mut valid_next_moves = vec![];
        for next_floor in self.next_floors() {
            for combo in self.elevator_combos() {
                let mut bs = (*self).clone();
                for &c in combo.iter() {
                    bs.floors[self.elevator_floor].remove(&c);
                    bs.floors[next_floor].insert(c);
                }
                bs.elevator_floor = next_floor;
                if !bs.has_fried_chips() && !seen.contains(&bs) {
                    valid_next_moves.push(bs);
                }
            }
        }
        valid_next_moves
    }

    pub fn has_fried_chips(&self) -> bool {
        for floor in self.floors.iter() {
            for component in floor {
                // This is doing an extra comparison to itself :shrug:
                if component.is_fried(floor) {
                    return true
                }
            }
        }
        false
    }
}

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

    #[test]
    fn not_in_end_state() {
        let mut floor1 = HashSet::new();
        floor1.insert(Component::Microchip(Element::Hydrogen));
        floor1.insert(Component::Microchip(Element::Lithium));

        let mut floor2 = HashSet::new();
        floor2.insert(Component::Generator(Element::Hydrogen));

        let mut floor3 = HashSet::new();
        floor3.insert(Component::Generator(Element::Lithium));

        let world_state = WorldState {
            steps: 0,
            building: BuildingState {
                elevator_floor: 0,
                floors: [
                    floor1,
                    floor2,
                    floor3,
                    HashSet::new(),
                ],
            }
        };
        assert!( ! world_state.in_end_state() );
    }

    #[test]
    fn in_end_state() {
        let mut floor4 = HashSet::new();
        floor4.insert(Component::Generator(Element::Hydrogen));
        floor4.insert(Component::Microchip(Element::Hydrogen));
        floor4.insert(Component::Microchip(Element::Lithium));
        floor4.insert(Component::Generator(Element::Lithium));
        let world_state = WorldState {
            steps: 0,
            building: BuildingState {
                elevator_floor: 0,
                floors: [
                    HashSet::new(),
                    HashSet::new(),
                    HashSet::new(),
                    floor4,
                ],
            }
        };

        assert!(world_state.in_end_state());
    }

    #[test]
    fn two_microchips_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Microchip(Element::Lithium));
        assert!(
            ! Component::Microchip(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn microchip_alone_not_fried() {
        assert!(
            ! Component::Microchip(Element::Hydrogen).is_fried(
                &HashSet::new()
            )
        )
    }

    #[test]
    fn microchip_with_itself_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Microchip(Element::Hydrogen));

        assert!(
            ! Component::Microchip(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn two_generators_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Lithium));

        assert!(
            ! Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn microchip_and_matching_generator_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Hydrogen));

        assert!(
            ! Component::Microchip(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn microchip_attached_to_matching_generator_not_fried_by_other_generator() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Lithium));
        hs.insert(Component::Generator(Element::Hydrogen));

        assert!(
            ! Component::Microchip(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn microchip_without_its_generator_with_another_generator_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Lithium));

        assert!(
            Component::Microchip(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn generator_with_a_microchip_without_its_generator_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Microchip(Element::Lithium));

        assert!(
            Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn generator_with_a_generator_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Lithium));
        assert!(
            ! Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn generator_with_its_microchip_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Microchip(Element::Hydrogen));

        assert!(
            ! Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn generator_with_itself_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Hydrogen));

        assert!(
            ! Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn generator_with_a_microchip_with_its_generator_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Lithium));
        hs.insert(Component::Microchip(Element::Lithium));

        assert!(
            ! Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }
}
<commit_msg>Still works with the sample input<commit_after>use std::collections::HashSet;

pub fn puzzle(input: &str) -> u32 {
    // let mut floor1 = HashSet::new();
    // floor1.insert(Component::Generator(Element::Thulium));
    // floor1.insert(Component::Microchip(Element::Thulium));
    // floor1.insert(Component::Generator(Element::Plutonium));
    // floor1.insert(Component::Generator(Element::Strontium));
    //
    // let mut floor2 = HashSet::new();
    // floor2.insert(Component::Microchip(Element::Plutonium));
    // floor2.insert(Component::Microchip(Element::Strontium));
    //
    // let mut floor3 = HashSet::new();
    // floor3.insert(Component::Generator(Element::Promethium));
    // floor3.insert(Component::Microchip(Element::Promethium));
    // floor3.insert(Component::Generator(Element::Ruthenium));
    // floor3.insert(Component::Microchip(Element::Ruthenium));

    let mut floor1 = HashSet::new();
    floor1.insert(Component::Microchip(Element::Hydrogen));
    floor1.insert(Component::Microchip(Element::Lithium));

    let mut floor2 = HashSet::new();
    floor2.insert(Component::Generator(Element::Hydrogen));

    let mut floor3 = HashSet::new();
    floor3.insert(Component::Generator(Element::Lithium));

    let initial_building_state = BuildingState {
        elevator_floor: 0,
        floors: [
            floor1,
            floor2,
            floor3,
            HashSet::new(),
        ],
    };
    let initial_world_state = WorldState {
        steps: 0,
        building: initial_building_state.clone()
    };
    let mut queue = vec![initial_world_state];
    let mut seen = vec![initial_building_state];
    let mut steps = 0;

    while !queue.is_empty() {
        let world = queue.remove(0);

        if world.steps != steps {
            println!("{} step(s), queue: {}", world.steps, queue.len());
            steps = world.steps;
        }

        seen.push(world.building.clone());

        if world.steps == 100 {
            panic!("too far!");
        }
        if world.in_end_state() {
            return world.steps;
        }

        let mut valid_next_moves = world.next_moves(&seen);
        queue.append(&mut valid_next_moves);
    }

    panic!("Exhausted all possible moves without finding end condition!");
}

#[derive(Debug)]
pub struct WorldState {
    steps: u32,
    building: BuildingState,
}

impl WorldState {
    pub fn in_end_state(&self) -> bool {
        self.building.in_end_state()
    }

    pub fn next_moves(&self, seen: &Vec<BuildingState>) -> Vec<WorldState> {
        self.building.next_moves(seen).into_iter().map(|b| {
            WorldState {
                steps: self.steps + 1,
                building: b,
            }
        }).collect()
    }
}

#[derive(PartialEq, Eq, Copy, Clone, Debug, PartialOrd, Ord, Hash)]
pub enum Component {
    Microchip(Element),
    Generator(Element),
}

impl Component {
    fn is_fried(&self, others: &HashSet<Component>) -> bool {
        match *self {
            Component::Generator(my_element) => {
                // There's microchip(s) that are not my element
                // and no generator that is that microchip's element
                (*others).iter().any(|&c|
                    match c {
                        Component::Microchip(e) if e != my_element && !(*others).contains(&Component::Generator(e)) => true,
                        _ => false,
                    }
                )
            },
            Component::Microchip(my_element) => {
                // There's some generator that is not my element
                (*others).iter().find(|&c| {
                    match *c {
                        Component::Generator(e) if e != my_element => true,
                        _ => false,
                    }
                }).is_some() &&
                // There is no generator that is my element
                (*others).iter().find(|&c| {
                    match *c {
                        Component::Generator(e) if e == my_element => true,
                        _ => false,
                    }
                }).is_none()
            }
        }
    }
}

#[derive(PartialEq, Eq, Copy, Clone, Debug, PartialOrd, Ord, Hash)]
pub enum Element {
    Hydrogen,
    Lithium,
    Thulium,
    Plutonium,
    Strontium,
    Promethium,
    Ruthenium,
}

#[derive(Debug, PartialEq)]
pub struct BuildingState {
    elevator_floor: usize,
    floors: [HashSet<Component>; 4],
}

// impl PartialEq for BuildingState {
//     fn eq(&self, other: &BuildingState) -> bool {
//         if self.elevator_floor != other.elevator_floor {
//             return false;
//         }
//         for (self_floor, other_floor) in self.floors.iter().zip(other.floors.iter()) {
//             let mut self_floor = self_floor.clone();
//             let mut other_floor = other_floor.clone();
//             self_floor.sort();
//             other_floor.sort();
//             if self_floor != other_floor {
//                 return false;
//             }
//         }
//
//         true
//     }
// }

impl Clone for BuildingState {
    fn clone(&self) -> BuildingState {
        BuildingState {
            elevator_floor: self.elevator_floor,
            floors: [
                self.floors[0].clone(),
                self.floors[1].clone(),
                self.floors[2].clone(),
                self.floors[3].clone(),
            ]
        }
    }
}

impl BuildingState {
    pub fn in_end_state(&self) -> bool {
        self.floors[0].is_empty() &&
            self.floors[1].is_empty() &&
            self.floors[2].is_empty()
    }

    pub fn next_floors(&self) -> Vec<usize> {
        match self.elevator_floor {
            0 => vec![1],
            1 => vec![0, 2],
            2 => vec![1, 3],
            3 => vec![2],
            other => panic!("How did you get on floor {}, elevator?", other),
        }
    }

    pub fn elevator_combos(&self) -> Vec<Vec<Component>> {
        // It sure would be nice to be able to have HashSet<HashSet<Component>>
        // here so that I don't have to filter out duplicates myself.

        let mut result = vec![];
        let mut components = self.floors[self.elevator_floor].iter();
        while let Some(&component) = components.next() {
            // Could take just this item on the elevator
            result.push(vec![component]);
            // Could take this item and one other, if they don't fry
            for &remaining_component in components.clone() {
                let mut hs = HashSet::new();
                hs.insert(remaining_component);
                if !component.is_fried(&hs) {
                    result.push(vec![component, remaining_component]);
                }
            }
        }
        result
    }

    pub fn next_moves(&self, seen: &Vec<BuildingState>) -> Vec<BuildingState> {
        let mut valid_next_moves = vec![];
        for next_floor in self.next_floors() {
            for combo in self.elevator_combos() {
                let mut bs = (*self).clone();
                for &c in combo.iter() {
                    bs.floors[self.elevator_floor].remove(&c);
                    bs.floors[next_floor].insert(c);
                }
                bs.elevator_floor = next_floor;
                if !bs.has_fried_chips() && !seen.contains(&bs) {
                    valid_next_moves.push(bs);
                }
            }
        }
        valid_next_moves
    }

    pub fn has_fried_chips(&self) -> bool {
        for floor in self.floors.iter() {
            for component in floor {
                // This is doing an extra comparison to itself :shrug:
                if component.is_fried(floor) {
                    return true
                }
            }
        }
        false
    }
}

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

    #[test]
    fn not_in_end_state() {
        let mut floor1 = HashSet::new();
        floor1.insert(Component::Microchip(Element::Hydrogen));
        floor1.insert(Component::Microchip(Element::Lithium));

        let mut floor2 = HashSet::new();
        floor2.insert(Component::Generator(Element::Hydrogen));

        let mut floor3 = HashSet::new();
        floor3.insert(Component::Generator(Element::Lithium));

        let world_state = WorldState {
            steps: 0,
            building: BuildingState {
                elevator_floor: 0,
                floors: [
                    floor1,
                    floor2,
                    floor3,
                    HashSet::new(),
                ],
            }
        };
        assert!( ! world_state.in_end_state() );
    }

    #[test]
    fn in_end_state() {
        let mut floor4 = HashSet::new();
        floor4.insert(Component::Generator(Element::Hydrogen));
        floor4.insert(Component::Microchip(Element::Hydrogen));
        floor4.insert(Component::Microchip(Element::Lithium));
        floor4.insert(Component::Generator(Element::Lithium));
        let world_state = WorldState {
            steps: 0,
            building: BuildingState {
                elevator_floor: 0,
                floors: [
                    HashSet::new(),
                    HashSet::new(),
                    HashSet::new(),
                    floor4,
                ],
            }
        };

        assert!(world_state.in_end_state());
    }

    #[test]
    fn two_microchips_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Microchip(Element::Lithium));
        assert!(
            ! Component::Microchip(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn microchip_alone_not_fried() {
        assert!(
            ! Component::Microchip(Element::Hydrogen).is_fried(
                &HashSet::new()
            )
        )
    }

    #[test]
    fn microchip_with_itself_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Microchip(Element::Hydrogen));

        assert!(
            ! Component::Microchip(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn two_generators_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Lithium));

        assert!(
            ! Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn microchip_and_matching_generator_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Hydrogen));

        assert!(
            ! Component::Microchip(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn microchip_attached_to_matching_generator_not_fried_by_other_generator() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Lithium));
        hs.insert(Component::Generator(Element::Hydrogen));

        assert!(
            ! Component::Microchip(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn microchip_without_its_generator_with_another_generator_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Lithium));

        assert!(
            Component::Microchip(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn generator_with_a_microchip_without_its_generator_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Microchip(Element::Lithium));

        assert!(
            Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn generator_with_a_generator_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Lithium));
        assert!(
            ! Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn generator_with_its_microchip_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Microchip(Element::Hydrogen));

        assert!(
            ! Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn generator_with_itself_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Hydrogen));

        assert!(
            ! Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }

    #[test]
    fn generator_with_a_microchip_with_its_generator_not_fried() {
        let mut hs = HashSet::new();
        hs.insert(Component::Generator(Element::Lithium));
        hs.insert(Component::Microchip(Element::Lithium));

        assert!(
            ! Component::Generator(Element::Hydrogen).is_fried(
                &hs
            )
        )
    }
}
<|endoftext|>


<commit_before>const OUT_OF_RANGE_ERR: &'static str = "Computus is only valid from 1583 to 9999";

pub mod gregorian {
    /// Easter in the Gregorian calendar
    pub fn month_day(year: i32) -> Result<(u32, u32), &'static str> {
        if year < 1583 || year > 9999 {
            return Err(::OUT_OF_RANGE_ERR);
        }
        let aa = year % 19;
        let bb = year / 100;
        let cc = year % 100;
        let dd = bb / 4;
        let ee = bb % 4;
        let ff = (bb + 8) / 25;
        let gg = (bb - ff + 1) / 3;
        let hh = (19 * aa + bb - dd - gg + 15) % 30;
        let ii = cc / 4;
        let kk = cc % 4;
        let ll = (32 + 2 * ee + 2 * ii - hh - kk) % 7;
        let mm = (aa + 11 * hh + 22 * ll) / 451;
        let month = (hh + ll - 7 * mm + 114) / 31;
        let day = (hh + ll - 7 * mm + 114) % 31 + 1;
        Ok((month as u32, day as u32))
    }
}

pub mod julian {
    /// Easter in the Julian calendar
    pub fn month_day(year: i32) -> Result<(u32, u32), &'static str> {
        if year < 1583 || year > 9999 {
            return Err(::OUT_OF_RANGE_ERR);
        }
        let aa = year % 4;
        let bb = year % 7;
        let cc = year % 19;
        let dd = (19 * cc + 15) % 30;
        let ee = (2 * aa + 4 * bb - dd + 34) % 7;
        let ff = dd + ee + 114;
        let month = ff / 31;
        let day = ff % 31 + 1;
        Ok((month as u32, day as u32))
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn gregorian_month_day() {
        use super::gregorian::month_day as gregorian;
        assert_eq!(gregorian(1961), Ok((4, 2)));
        assert_eq!(gregorian(1996), Ok((4, 7)));
        assert_eq!(gregorian(1997), Ok((3, 30)));
        assert_eq!(gregorian(1998), Ok((4, 12)));
        assert_eq!(gregorian(2000), Ok((4, 23)));
        assert_eq!(gregorian(2001), Ok((4, 15)));
        assert_eq!(gregorian(2002), Ok((3, 31)));
        assert_eq!(gregorian(2003), Ok((4, 20)));
        assert_eq!(gregorian(2004), Ok((4, 11)));
        assert_eq!(gregorian(2005), Ok((3, 27)));
        assert_eq!(gregorian(2006), Ok((4, 16)));
        assert_eq!(gregorian(2007), Ok((4, 8)));
        assert_eq!(gregorian(2008), Ok((3, 23)));
        assert_eq!(gregorian(2009), Ok((4, 12)));
        assert_eq!(gregorian(2010), Ok((4, 4)));
        assert_eq!(gregorian(2011), Ok((4, 24)));
        assert_eq!(gregorian(2012), Ok((4, 8)));
        assert_eq!(gregorian(2013), Ok((3, 31)));
        assert_eq!(gregorian(2014), Ok((4, 20)));
        assert_eq!(gregorian(2015), Ok((4, 5)));
        assert_eq!(gregorian(2016), Ok((3, 27)));
        assert_eq!(gregorian(2017), Ok((4, 16)));
        assert_eq!(gregorian(2018), Ok((4, 1)));
        assert_eq!(gregorian(2019), Ok((4, 21)));
        assert_eq!(gregorian(2020), Ok((4, 12)));
        assert_eq!(gregorian(2021), Ok((4, 4)));
        assert_eq!(gregorian(2022), Ok((4, 17)));
        assert_eq!(gregorian(3035), Ok((4, 19)));
        assert_eq!(gregorian(4000), Ok((4, 9)));
        assert_eq!(gregorian(9999), Ok((3, 28)));
    }

    #[test]
    fn julian_month_day() {
        use super::julian::month_day as julian;
        assert_eq!(julian(1961), Ok((3, 27)));
        assert_eq!(julian(1996), Ok((4, 1)));
        assert_eq!(julian(1997), Ok((4, 14)));
        assert_eq!(julian(1998), Ok((4, 6)));
        assert_eq!(julian(1999), Ok((3, 29)));
        assert_eq!(julian(2000), Ok((4, 17)));
        assert_eq!(julian(2001), Ok((4, 2)));
        assert_eq!(julian(2002), Ok((4, 22)));
        assert_eq!(julian(2003), Ok((4, 14)));
        assert_eq!(julian(2004), Ok((3, 29)));
        assert_eq!(julian(2005), Ok((4, 18)));
        assert_eq!(julian(2006), Ok((4, 10)));
        assert_eq!(julian(2007), Ok((3, 26)));
        assert_eq!(julian(2008), Ok((4, 14)));
        assert_eq!(julian(2009), Ok((4, 6)));
        assert_eq!(julian(2010), Ok((3, 22)));
        assert_eq!(julian(2011), Ok((4, 11)));
        assert_eq!(julian(2012), Ok((4, 2)));
        assert_eq!(julian(2013), Ok((4, 22)));
        assert_eq!(julian(2014), Ok((4, 7)));
        assert_eq!(julian(2015), Ok((3, 30)));
        assert_eq!(julian(2016), Ok((4, 18)));
        assert_eq!(julian(2017), Ok((4, 3)));
        assert_eq!(julian(2018), Ok((3, 26)));
        assert_eq!(julian(2019), Ok((4, 15)));
        assert_eq!(julian(2020), Ok((4, 6)));
    }
}
<commit_msg>return Date struct<commit_after>#[derive(Debug, Eq, PartialEq)]
pub struct Date {
    pub year: i32,
    pub month: u32,
    pub day: u32,
}

impl Date {
    pub fn ymd(y: i32, m: u32, d: u32) -> Self {
        Date {
            year: y,
            month: m,
            day: d,
        }
    }
}

const OUT_OF_RANGE_ERR: &'static str = "Computus is only valid from 1583 to 9999";

pub mod gregorian {
    /// Easter in the Gregorian calendar
    pub fn month_day(year: i32) -> Result<::Date, &'static str> {
        if year < 1583 || year > 9999 {
            return Err(::OUT_OF_RANGE_ERR);
        }
        let aa = year % 19;
        let bb = year / 100;
        let cc = year % 100;
        let dd = bb / 4;
        let ee = bb % 4;
        let ff = (bb + 8) / 25;
        let gg = (bb - ff + 1) / 3;
        let hh = (19 * aa + bb - dd - gg + 15) % 30;
        let ii = cc / 4;
        let kk = cc % 4;
        let ll = (32 + 2 * ee + 2 * ii - hh - kk) % 7;
        let mm = (aa + 11 * hh + 22 * ll) / 451;
        let month = (hh + ll - 7 * mm + 114) / 31;
        let day = (hh + ll - 7 * mm + 114) % 31 + 1;
        Ok(::Date::ymd(year, month as u32, day as u32))
    }
}

pub mod julian {
    /// Easter in the Julian calendar
    pub fn month_day(year: i32) -> Result<::Date, &'static str> {
        if year < 1583 || year > 9999 {
            return Err(::OUT_OF_RANGE_ERR);
        }
        let aa = year % 4;
        let bb = year % 7;
        let cc = year % 19;
        let dd = (19 * cc + 15) % 30;
        let ee = (2 * aa + 4 * bb - dd + 34) % 7;
        let ff = dd + ee + 114;
        let month = ff / 31;
        let day = ff % 31 + 1;
        Ok(::Date::ymd(year, month as u32, day as u32))
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn gregorian_month_day() {
        use ::Date;
        use super::gregorian::month_day as gregorian;
        assert_eq!(gregorian(1961), Ok(Date::ymd(1961, 4, 2)));
        assert_eq!(gregorian(1996), Ok(Date::ymd(1996, 4, 7)));
        assert_eq!(gregorian(1997), Ok(Date::ymd(1997, 3, 30)));
        assert_eq!(gregorian(1998), Ok(Date::ymd(1998, 4, 12)));
        assert_eq!(gregorian(2000), Ok(Date::ymd(2000, 4, 23)));
        assert_eq!(gregorian(2001), Ok(Date::ymd(2001, 4, 15)));
        assert_eq!(gregorian(2002), Ok(Date::ymd(2002, 3, 31)));
        assert_eq!(gregorian(2003), Ok(Date::ymd(2003, 4, 20)));
        assert_eq!(gregorian(2004), Ok(Date::ymd(2004, 4, 11)));
        assert_eq!(gregorian(2005), Ok(Date::ymd(2005, 3, 27)));
        assert_eq!(gregorian(2006), Ok(Date::ymd(2006, 4, 16)));
        assert_eq!(gregorian(2007), Ok(Date::ymd(2007, 4, 8)));
        assert_eq!(gregorian(2008), Ok(Date::ymd(2008, 3, 23)));
        assert_eq!(gregorian(2009), Ok(Date::ymd(2009, 4, 12)));
        assert_eq!(gregorian(2010), Ok(Date::ymd(2010, 4, 4)));
        assert_eq!(gregorian(2011), Ok(Date::ymd(2011, 4, 24)));
        assert_eq!(gregorian(2012), Ok(Date::ymd(2012, 4, 8)));
        assert_eq!(gregorian(2013), Ok(Date::ymd(2013, 3, 31)));
        assert_eq!(gregorian(2014), Ok(Date::ymd(2014, 4, 20)));
        assert_eq!(gregorian(2015), Ok(Date::ymd(2015, 4, 5)));
        assert_eq!(gregorian(2016), Ok(Date::ymd(2016, 3, 27)));
        assert_eq!(gregorian(2017), Ok(Date::ymd(2017, 4, 16)));
        assert_eq!(gregorian(2018), Ok(Date::ymd(2018, 4, 1)));
        assert_eq!(gregorian(2019), Ok(Date::ymd(2019, 4, 21)));
        assert_eq!(gregorian(2020), Ok(Date::ymd(2020, 4, 12)));
        assert_eq!(gregorian(2021), Ok(Date::ymd(2021, 4, 4)));
        assert_eq!(gregorian(2022), Ok(Date::ymd(2022, 4, 17)));
        assert_eq!(gregorian(3035), Ok(Date::ymd(3035, 4, 19)));
        assert_eq!(gregorian(4000), Ok(Date::ymd(4000, 4, 9)));
        assert_eq!(gregorian(9999), Ok(Date::ymd(9999, 3, 28)));
    }

    #[test]
    fn julian_month_day() {
        use ::Date;
        use super::julian::month_day as julian;
        assert_eq!(julian(1961), Ok(Date::ymd(1961, 3, 27)));
        assert_eq!(julian(1996), Ok(Date::ymd(1996, 4, 1)));
        assert_eq!(julian(1997), Ok(Date::ymd(1997, 4, 14)));
        assert_eq!(julian(1998), Ok(Date::ymd(1998, 4, 6)));
        assert_eq!(julian(1999), Ok(Date::ymd(1999, 3, 29)));
        assert_eq!(julian(2000), Ok(Date::ymd(2000, 4, 17)));
        assert_eq!(julian(2001), Ok(Date::ymd(2001, 4, 2)));
        assert_eq!(julian(2002), Ok(Date::ymd(2002, 4, 22)));
        assert_eq!(julian(2003), Ok(Date::ymd(2003, 4, 14)));
        assert_eq!(julian(2004), Ok(Date::ymd(2004, 3, 29)));
        assert_eq!(julian(2005), Ok(Date::ymd(2005, 4, 18)));
        assert_eq!(julian(2006), Ok(Date::ymd(2006, 4, 10)));
        assert_eq!(julian(2007), Ok(Date::ymd(2007, 3, 26)));
        assert_eq!(julian(2008), Ok(Date::ymd(2008, 4, 14)));
        assert_eq!(julian(2009), Ok(Date::ymd(2009, 4, 6)));
        assert_eq!(julian(2010), Ok(Date::ymd(2010, 3, 22)));
        assert_eq!(julian(2011), Ok(Date::ymd(2011, 4, 11)));
        assert_eq!(julian(2012), Ok(Date::ymd(2012, 4, 2)));
        assert_eq!(julian(2013), Ok(Date::ymd(2013, 4, 22)));
        assert_eq!(julian(2014), Ok(Date::ymd(2014, 4, 7)));
        assert_eq!(julian(2015), Ok(Date::ymd(2015, 3, 30)));
        assert_eq!(julian(2016), Ok(Date::ymd(2016, 4, 18)));
        assert_eq!(julian(2017), Ok(Date::ymd(2017, 4, 3)));
        assert_eq!(julian(2018), Ok(Date::ymd(2018, 3, 26)));
        assert_eq!(julian(2019), Ok(Date::ymd(2019, 4, 15)));
        assert_eq!(julian(2020), Ok(Date::ymd(2020, 4, 6)));
    }
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]
#![allow(unstable)]

//! A table object type for dynamical data

use std::collections::HashMap;
use std::sync::Arc;
use std::ops::{ Deref, DerefMut, Index, IndexMut };
use std::hash::{ Hash, Hasher, Writer };
use std::borrow::BorrowFrom;

/// Represents a dynamical typed value
#[derive(Clone, PartialEq, Eq, Show)]
pub enum Value {
    /// An empty value.
    Null,
    /// A boolean value.
    Bool(bool),
    /// A pointer sized integer.
    Usize(usize),
    /// A 64 bit unsigned integer.
    U64(u64),
    /// A 64 bit signed integer.
    I64(i64),
    /// A 64 bit floating number.
    F64(F64),
    /// A string.
    String(Arc<String>),
    /// A table.
    Table(Arc<Table>),
}

impl Value {
    /// Creates a new f64 value.
    pub fn f64(val: f64) -> Value {
        Value::F64(F64(val))
    }
}

impl<S> Hash<S> for Value
    where
        S: Hasher + Writer
{
    fn hash(&self, state: &mut S) {
        match self {
            &Value::String(ref text) => text.hash(state),
            &Value::Null => 0us.hash(state),
            &Value::Bool(val) => val.hash(state),
            &Value::Usize(val) => val.hash(state),
            &Value::U64(val) => val.hash(state),
            &Value::I64(val) => val.hash(state),
            &Value::F64(val) => val.hash(state),
            &Value::Table(ref val) => val.hash(state),
        }
    }
}

impl BorrowFrom<Value> for str {
    fn borrow_from(owned: &Value) -> &str {
        match owned {
            &Value::String(ref text) => {
                &text[]
            }
            _ => ""
        }
    }
}

/// Wrapper for f64
#[derive(Copy, Clone, PartialEq, Show)]
pub struct F64(pub f64);

impl Eq for F64 {}

impl<S> Hash<S> for F64
    where
        S: Hasher + Writer
{
    fn hash(&self, state: &mut S) {
        let val = self.0 as u64;
        val.hash(state)
    }
}

impl Deref for F64 {
    type Target = f64;

    fn deref<'a>(&'a self) -> &'a f64 {
        &self.0
    }
}

impl DerefMut for F64 {
    fn deref_mut<'a>(&'a mut self) -> &'a mut f64 {
        &mut self.0
    }
}

/// The table object
#[derive(Clone, PartialEq, Eq, Show)]
pub struct Table(pub HashMap<Value, Value>);

impl<S> Hash<S> for Table
    where
        S: Hasher + Writer
{
    fn hash(&self, state: &mut S) {
        for (key, val) in self.0.iter() {
            key.hash(state);
            val.hash(state);
        }
    }
}

impl Deref for Table {
    type Target = HashMap<Value, Value>;

    fn deref<'a>(&'a self) -> &'a HashMap<Value, Value> {
        &self.0
    }
}

impl DerefMut for Table {
    fn deref_mut<'a>(&'a mut self) -> &'a mut HashMap<Value, Value> {
        &mut self.0
    }
}

impl Index<Value> for Table {
    type Output = Value;

    fn index<'a>(&'a self, index: &Value) -> &'a Value {
        self.0.get(index).unwrap()
    }
}

impl Index<usize> for Table {
    type Output = Value;

    fn index<'a>(&'a self, index: &usize) -> &'a Value {
        self.0.get(&Value::Usize(*index)).unwrap()
    }
}

impl<'b> Index<&'b str> for Table {
    type Output = Value;

    fn index<'a>(&'a self, index: &&'b str) -> &'a Value {
        self.0.get(*index).unwrap()
    }
}

impl IndexMut<Value> for Table {
    type Output = Value;

    fn index_mut<'a>(&'a mut self, index: &Value) -> &'a mut Value {
        use std::collections::hash_map::Entry;

        match self.0.entry(index.clone()) {
            Entry::Occupied(_) => {},
            Entry::Vacant(x) => {x.insert(Value::Null);}
        };
        self.0.get_mut(index).unwrap()
    }
}

impl IndexMut<usize> for Table {
    type Output = Value;

    fn index_mut<'a>(&'a mut self, index: &usize) -> &'a mut Value {
        use std::collections::hash_map::Entry;

        match self.0.entry(Value::Usize(*index)) {
            Entry::Occupied(_) => {},
            Entry::Vacant(x) => {x.insert(Value::Null);}
        };
        self.0.get_mut(&Value::Usize(*index)).unwrap()
    }
}

impl<'b> IndexMut<&'b str> for Table {
    type Output = Value;

    fn index_mut<'a>(&'a mut self, index: &&'b str) -> &'a mut Value {
        use std::borrow::ToOwned;

        if !self.0.contains_key(*index) {
            self.insert(Value::String(Arc::new((*index).to_owned())),
                Value::Null);
        }
        self.0.get_mut(*index).unwrap()
    }
}

impl Table {
    /// Creates new table.
    pub fn new() -> Table {
        Table(HashMap::new())
    }

    /// Creates new table with capacity.
    pub fn with_capacity(capacity: usize) -> Table {
        Table(HashMap::with_capacity(capacity))
    }
}

#[cfg(test)]
mod tests {
    extern crate test;

    use super::*;
    use self::test::Bencher;
    use std::sync::Arc;

    #[test]
    fn test_size() {
        use std::mem::size_of;

        // Make sure `Value` is not exceeding 128 bits on 64 bit systems.
        // This is for performance reasons.
        if size_of::<usize>() == 8 {
            assert_eq!(size_of::<Value>(), 16);
        }
    }

    #[test]
    fn test_sync() {
        fn foo<T: Sync>() {}

        // Make sure `Value` can be shared among tasks.
        foo::<Value>();
        foo::<Table>();
    }

    #[test]
    fn test_vec3() {
        let mut vec3 = Table::with_capacity(3);
        vec3[0us] = Value::f64(1.0);
        vec3[1us] = Value::f64(2.0);
        vec3[2us] = Value::f64(3.0);
        assert_eq!(vec3[0us], Value::f64(1.0));
        assert_eq!(vec3[1us], Value::f64(2.0));
        assert_eq!(vec3[2us], Value::f64(3.0));
        vec3.clear();
        assert_eq!(vec3.len(), 0);
    }

    #[test]
    fn test_table_as_key() {
        use std::borrow::ToOwned;

        let a = Value::Table(Arc::new(Table::new()));
        let mut b = Table::new();
        b[a] = Value::String(Arc::new("hello".to_owned()));
    }

    #[test]
    fn test_str_as_key() {
        use std::borrow::ToOwned;

        let mut a = Table::new();
        a.insert(Value::String(Arc::new("hello".to_owned())),
            Value::String(Arc::new("world".to_owned())));
        assert!(
            if let Some(&Value::String(_)) = a.get("hello") { true }
            else { false }
        );
        assert_eq!(a["hello"], Value::String(Arc::new("world".to_owned())));
        a["hello2"] = Value::String(Arc::new("world".to_owned()));
        assert_eq!(a["hello"], a["hello2"]);
    }

    #[bench]
    fn bench_create_empty(bencher: &mut Bencher) {
        bencher.iter(|| {
            let _ = Table::new();
        });
    }

    #[bench]
    fn bench_create_empty_arc(bencher: &mut Bencher) {
        use std::sync::Arc;

        bencher.iter(|| {
            let _ = Arc::new(Table::new());
        });
    }

    #[bench]
    fn bench_overwrite(bencher: &mut Bencher) {
        let mut a = Table::new();
        bencher.iter(|| {
            a[0us] = Value::f64(1.0);
        });
    }

    #[bench]
    fn bench_overwrite_arc(bencher: &mut Bencher) {
        use std::sync::Arc;

        let mut a = Arc::new(Table::new());
        bencher.iter(|| {
            a.make_unique()[0us] = Value::f64(1.0);
        });
    }
}

<commit_msg>Added `Value::str` and `Value::table`<commit_after>#![deny(missing_docs)]
#![allow(unstable)]

//! A table object type for dynamical data

use std::collections::HashMap;
use std::sync::Arc;
use std::ops::{ Deref, DerefMut, Index, IndexMut };
use std::hash::{ Hash, Hasher, Writer };
use std::borrow::BorrowFrom;

/// Represents a dynamical typed value
#[derive(Clone, PartialEq, Eq, Show)]
pub enum Value {
    /// An empty value.
    Null,
    /// A boolean value.
    Bool(bool),
    /// A pointer sized integer.
    Usize(usize),
    /// A 64 bit unsigned integer.
    U64(u64),
    /// A 64 bit signed integer.
    I64(i64),
    /// A 64 bit floating number.
    F64(F64),
    /// A string.
    String(Arc<String>),
    /// A table.
    Table(Arc<Table>),
}

impl Value {
    /// Creates a new f64 value.
    pub fn f64(val: f64) -> Value {
        Value::F64(F64(val))
    }

    /// Creates a new string value.
    pub fn str(val: &str) -> Value {
        use std::borrow::ToOwned;

        Value::String(Arc::new(val.to_owned()))
    }

    /// Creates a new table value.
    pub fn table(val: Table) -> Value {
        Value::Table(Arc::new(val))
    }
}

impl<S> Hash<S> for Value
    where
        S: Hasher + Writer
{
    fn hash(&self, state: &mut S) {
        match self {
            &Value::String(ref text) => text.hash(state),
            &Value::Null => 0us.hash(state),
            &Value::Bool(val) => val.hash(state),
            &Value::Usize(val) => val.hash(state),
            &Value::U64(val) => val.hash(state),
            &Value::I64(val) => val.hash(state),
            &Value::F64(val) => val.hash(state),
            &Value::Table(ref val) => val.hash(state),
        }
    }
}

impl BorrowFrom<Value> for str {
    fn borrow_from(owned: &Value) -> &str {
        match owned {
            &Value::String(ref text) => {
                &text[]
            }
            _ => ""
        }
    }
}

/// Wrapper for f64
#[derive(Copy, Clone, PartialEq, Show)]
pub struct F64(pub f64);

impl Eq for F64 {}

impl<S> Hash<S> for F64
    where
        S: Hasher + Writer
{
    fn hash(&self, state: &mut S) {
        let val = self.0 as u64;
        val.hash(state)
    }
}

impl Deref for F64 {
    type Target = f64;

    fn deref<'a>(&'a self) -> &'a f64 {
        &self.0
    }
}

impl DerefMut for F64 {
    fn deref_mut<'a>(&'a mut self) -> &'a mut f64 {
        &mut self.0
    }
}

/// The table object
#[derive(Clone, PartialEq, Eq, Show)]
pub struct Table(pub HashMap<Value, Value>);

impl<S> Hash<S> for Table
    where
        S: Hasher + Writer
{
    fn hash(&self, state: &mut S) {
        for (key, val) in self.0.iter() {
            key.hash(state);
            val.hash(state);
        }
    }
}

impl Deref for Table {
    type Target = HashMap<Value, Value>;

    fn deref<'a>(&'a self) -> &'a HashMap<Value, Value> {
        &self.0
    }
}

impl DerefMut for Table {
    fn deref_mut<'a>(&'a mut self) -> &'a mut HashMap<Value, Value> {
        &mut self.0
    }
}

impl Index<Value> for Table {
    type Output = Value;

    fn index<'a>(&'a self, index: &Value) -> &'a Value {
        self.0.get(index).unwrap()
    }
}

impl Index<usize> for Table {
    type Output = Value;

    fn index<'a>(&'a self, index: &usize) -> &'a Value {
        self.0.get(&Value::Usize(*index)).unwrap()
    }
}

impl<'b> Index<&'b str> for Table {
    type Output = Value;

    fn index<'a>(&'a self, index: &&'b str) -> &'a Value {
        self.0.get(*index).unwrap()
    }
}

impl IndexMut<Value> for Table {
    type Output = Value;

    fn index_mut<'a>(&'a mut self, index: &Value) -> &'a mut Value {
        use std::collections::hash_map::Entry;

        match self.0.entry(index.clone()) {
            Entry::Occupied(_) => {},
            Entry::Vacant(x) => {x.insert(Value::Null);}
        };
        self.0.get_mut(index).unwrap()
    }
}

impl IndexMut<usize> for Table {
    type Output = Value;

    fn index_mut<'a>(&'a mut self, index: &usize) -> &'a mut Value {
        use std::collections::hash_map::Entry;

        match self.0.entry(Value::Usize(*index)) {
            Entry::Occupied(_) => {},
            Entry::Vacant(x) => {x.insert(Value::Null);}
        };
        self.0.get_mut(&Value::Usize(*index)).unwrap()
    }
}

impl<'b> IndexMut<&'b str> for Table {
    type Output = Value;

    fn index_mut<'a>(&'a mut self, index: &&'b str) -> &'a mut Value {
        use std::borrow::ToOwned;

        if !self.0.contains_key(*index) {
            self.insert(Value::String(Arc::new((*index).to_owned())),
                Value::Null);
        }
        self.0.get_mut(*index).unwrap()
    }
}

impl Table {
    /// Creates new table.
    pub fn new() -> Table {
        Table(HashMap::new())
    }

    /// Creates new table with capacity.
    pub fn with_capacity(capacity: usize) -> Table {
        Table(HashMap::with_capacity(capacity))
    }
}

#[cfg(test)]
mod tests {
    extern crate test;

    use super::*;
    use self::test::Bencher;
    use std::sync::Arc;

    #[test]
    fn test_size() {
        use std::mem::size_of;

        // Make sure `Value` is not exceeding 128 bits on 64 bit systems.
        // This is for performance reasons.
        if size_of::<usize>() == 8 {
            assert_eq!(size_of::<Value>(), 16);
        }
    }

    #[test]
    fn test_sync() {
        fn foo<T: Sync>() {}

        // Make sure `Value` can be shared among tasks.
        foo::<Value>();
        foo::<Table>();
    }

    #[test]
    fn test_vec3() {
        let mut vec3 = Table::with_capacity(3);
        vec3[0us] = Value::f64(1.0);
        vec3[1us] = Value::f64(2.0);
        vec3[2us] = Value::f64(3.0);
        assert_eq!(vec3[0us], Value::f64(1.0));
        assert_eq!(vec3[1us], Value::f64(2.0));
        assert_eq!(vec3[2us], Value::f64(3.0));
        vec3.clear();
        assert_eq!(vec3.len(), 0);
    }

    #[test]
    fn test_table_as_key() {
        use std::borrow::ToOwned;

        let a = Value::Table(Arc::new(Table::new()));
        let mut b = Table::new();
        b[a] = Value::String(Arc::new("hello".to_owned()));
    }

    #[test]
    fn test_str_as_key() {
        use std::borrow::ToOwned;

        let mut a = Table::new();
        a.insert(Value::String(Arc::new("hello".to_owned())),
            Value::String(Arc::new("world".to_owned())));
        assert!(
            if let Some(&Value::String(_)) = a.get("hello") { true }
            else { false }
        );
        assert_eq!(a["hello"], Value::String(Arc::new("world".to_owned())));
        a["hello2"] = Value::String(Arc::new("world".to_owned()));
        assert_eq!(a["hello"], a["hello2"]);
    }

    #[bench]
    fn bench_create_empty(bencher: &mut Bencher) {
        bencher.iter(|| {
            let _ = Table::new();
        });
    }

    #[bench]
    fn bench_create_empty_arc(bencher: &mut Bencher) {
        use std::sync::Arc;

        bencher.iter(|| {
            let _ = Arc::new(Table::new());
        });
    }

    #[bench]
    fn bench_overwrite(bencher: &mut Bencher) {
        let mut a = Table::new();
        bencher.iter(|| {
            a[0us] = Value::f64(1.0);
        });
    }

    #[bench]
    fn bench_overwrite_arc(bencher: &mut Bencher) {
        use std::sync::Arc;

        let mut a = Arc::new(Table::new());
        bencher.iter(|| {
            a.make_unique()[0us] = Value::f64(1.0);
        });
    }
}

<|endoftext|>


<commit_before>// Copyright(C) 2016 Chris Liebert
#![crate_type = "dylib"]
#![crate_name = "quick3d"]
#![allow(dead_code)]
#[macro_use]
extern crate glium;

pub mod common;

pub mod loader;
#[macro_use]
pub mod renderer;

extern crate nalgebra;
extern crate libc;

use std::ffi::CStr;
use std::os::raw::c_char;

use glium::glutin;
use glium::glutin::Event;
use glium::glutin::ElementState;
use glium::glutin::VirtualKeyCode;
use glium::DisplayBuild;

use glium::backend::glutin_backend::GlutinFacade;

use loader::DBLoader;
use renderer::Renderer;

#[no_mangle]
pub extern "C" fn create_window(screen_width: libc::int32_t,
                                screen_height: libc::int32_t,
                                title: *const c_char)
                                -> Box<GlutinFacade> {
    let w: u32 = screen_width as u32;
    let h: u32 = screen_height as u32;
    let window_title: String;

    unsafe {
        window_title = CStr::from_ptr(title).to_string_lossy().into_owned();
        let display: GlutinFacade = glutin::WindowBuilder::new()
	        //.resizable()
	        //.with_vsync()
	        //with_gl_debug_flag(true)
	        .with_title(window_title)
	        .with_visibility(true)
	        .with_dimensions(w, h)
	        .build_glium()
	        .unwrap();
        return Box::new(display);
    }
}

#[no_mangle]
pub extern "C" fn create_db_loader(filename_cstr: *const c_char) -> Box<DBLoader> {
    unsafe {
        let filename: String = CStr::from_ptr(filename_cstr).to_string_lossy().into_owned();
        let dbloader: DBLoader = DBLoader::new(&filename);
        println!("Loaded {}", &filename);
        return Box::new(dbloader);
    }
}

#[no_mangle]
pub extern "C" fn create_renderer_from_db_loader(dbloader: &DBLoader,
                                                 display: &GlutinFacade)
                                                 -> Box<Renderer> {
    return Box::new(renderer::Renderer::new(display, dbloader.load_scene()));
}

#[no_mangle]
pub extern "C" fn get_shader_from_db_loader(shader_name_cstr: *const c_char,
                                            dbloader: &DBLoader,
                                            renderer: &Renderer,
                                            display: &GlutinFacade)
                                            -> Box<glium::program::Program> {
    unsafe {
        let shader_name: String = CStr::from_ptr(shader_name_cstr).to_string_lossy().into_owned();
        let shader_program = renderer.create_shader_program(&shader_name, dbloader, display);
        return Box::new(shader_program);
    }
}

#[no_mangle]
pub extern "C" fn poll_quit_event(display: &GlutinFacade) -> libc::int32_t {
    let mut quit: libc::int32_t = 0;
    for event in display.poll_events() {
        match event {
            Event::Closed => quit = 1,
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Escape)) => {
                quit = 1
            }
            _ => (),
        }
    }
    if quit.clone() > 0 {
        println!("Polling quit event returned {}", quit.clone());
    }

    return quit;
}

#[no_mangle]
pub extern "C" fn render(renderer: &Renderer,
                         shader_program: &glium::program::Program,
                         display: &GlutinFacade) {
    renderer.render(display, shader_program);
}

#[no_mangle]
pub extern "C" fn take_memory(c_pointer: *mut libc::c_void) -> *mut libc::c_void {
    unsafe {
        let box_ptr = Box::from_raw(c_pointer);
        return Box::into_raw(box_ptr);
    }
}

#[cfg(test)]
mod tests {
    use glium::glutin;
    use glium::backend::glutin_backend::GlutinFacade;
    use glium::DisplayBuild;
    use loader;
    use common::Scene;
    use renderer;

    fn create_test_display() -> GlutinFacade {
        let display = glutin::WindowBuilder::new()
            .with_visibility(false)
            .with_gl_debug_flag(true)
            .build_glium()
            .unwrap();
        return display;
    }

    fn load_test_scene() -> Scene {
        let dbloader: loader::DBLoader = loader::DBLoader::new("test.db");
        return dbloader.load_scene();
    }

    #[test]
    fn loader_load_db_not_empty() {
        println!("Running loader_load_db_not_empty test");
        let scene: Scene = load_test_scene();
        assert!(scene.meshes.len() > 0);
        assert!(scene.materials.len() > 0);
        // assert!(scene.shaders.len() > 0);
    }

    #[test]
    fn load_and_render_db() {
        println!("Running load_and_render_db test");
        let display = create_test_display();
        let scene: Scene = load_test_scene();
        assert!(scene.meshes.len() > 0);
        assert!(scene.materials.len() > 0);
        // assert!(scene.shaders.len() > 0);
        // let renderer: renderer::Renderer = renderer::Renderer::new(&display, scene);
        // renderer.render(&display);
    }
}
<commit_msg>Commented out #[test] directive that was failing on Travis CI<commit_after>// Copyright(C) 2016 Chris Liebert
#![crate_type = "dylib"]
#![crate_name = "quick3d"]
#![allow(dead_code)]
#[macro_use]
extern crate glium;

pub mod common;

pub mod loader;
#[macro_use]
pub mod renderer;

extern crate nalgebra;
extern crate libc;

use std::ffi::CStr;
use std::os::raw::c_char;

use glium::glutin;
use glium::glutin::Event;
use glium::glutin::ElementState;
use glium::glutin::VirtualKeyCode;
use glium::DisplayBuild;

use glium::backend::glutin_backend::GlutinFacade;

use loader::DBLoader;
use renderer::Renderer;

#[no_mangle]
pub extern "C" fn create_window(screen_width: libc::int32_t,
                                screen_height: libc::int32_t,
                                title: *const c_char)
                                -> Box<GlutinFacade> {
    let w: u32 = screen_width as u32;
    let h: u32 = screen_height as u32;
    let window_title: String;

    unsafe {
        window_title = CStr::from_ptr(title).to_string_lossy().into_owned();
        let display: GlutinFacade = glutin::WindowBuilder::new()
	        //.resizable()
	        //.with_vsync()
	        //with_gl_debug_flag(true)
	        .with_title(window_title)
	        .with_visibility(true)
	        .with_dimensions(w, h)
	        .build_glium()
	        .unwrap();
        return Box::new(display);
    }
}

#[no_mangle]
pub extern "C" fn create_db_loader(filename_cstr: *const c_char) -> Box<DBLoader> {
    unsafe {
        let filename: String = CStr::from_ptr(filename_cstr).to_string_lossy().into_owned();
        let dbloader: DBLoader = DBLoader::new(&filename);
        println!("Loaded {}", &filename);
        return Box::new(dbloader);
    }
}

#[no_mangle]
pub extern "C" fn create_renderer_from_db_loader(dbloader: &DBLoader,
                                                 display: &GlutinFacade)
                                                 -> Box<Renderer> {
    return Box::new(renderer::Renderer::new(display, dbloader.load_scene()));
}

#[no_mangle]
pub extern "C" fn get_shader_from_db_loader(shader_name_cstr: *const c_char,
                                            dbloader: &DBLoader,
                                            renderer: &Renderer,
                                            display: &GlutinFacade)
                                            -> Box<glium::program::Program> {
    unsafe {
        let shader_name: String = CStr::from_ptr(shader_name_cstr).to_string_lossy().into_owned();
        let shader_program = renderer.create_shader_program(&shader_name, dbloader, display);
        return Box::new(shader_program);
    }
}

#[no_mangle]
pub extern "C" fn poll_quit_event(display: &GlutinFacade) -> libc::int32_t {
    let mut quit: libc::int32_t = 0;
    for event in display.poll_events() {
        match event {
            Event::Closed => quit = 1,
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Escape)) => {
                quit = 1
            }
            _ => (),
        }
    }
    if quit.clone() > 0 {
        println!("Polling quit event returned {}", quit.clone());
    }

    return quit;
}

#[no_mangle]
pub extern "C" fn render(renderer: &Renderer,
                         shader_program: &glium::program::Program,
                         display: &GlutinFacade) {
    renderer.render(display, shader_program);
}

#[no_mangle]
pub extern "C" fn take_memory(c_pointer: *mut libc::c_void) -> *mut libc::c_void {
    unsafe {
        let box_ptr = Box::from_raw(c_pointer);
        return Box::into_raw(box_ptr);
    }
}

#[cfg(test)]
mod tests {
    use glium::glutin;
    use glium::backend::glutin_backend::GlutinFacade;
    use glium::DisplayBuild;
    use loader;
    use common::Scene;
    use renderer;

    fn create_test_display() -> GlutinFacade {
        let display = glutin::WindowBuilder::new()
            .with_visibility(false)
            .with_gl_debug_flag(true)
            .build_glium()
            .unwrap();
        return display;
    }

    fn load_test_scene() -> Scene {
        let dbloader: loader::DBLoader = loader::DBLoader::new("test.db");
        return dbloader.load_scene();
    }

    #[test]
    fn loader_load_db_not_empty() {
        println!("Running loader_load_db_not_empty test");
        let scene: Scene = load_test_scene();
        assert!(scene.meshes.len() > 0);
        assert!(scene.materials.len() > 0);
        // assert!(scene.shaders.len() > 0);
    }

    //#[test]
    fn load_and_render_db() {
        println!("Running load_and_render_db test");
        let display = create_test_display();
        let scene: Scene = load_test_scene();
        assert!(scene.meshes.len() > 0);
        assert!(scene.materials.len() > 0);
        // assert!(scene.shaders.len() > 0);
        // let renderer: renderer::Renderer = renderer::Renderer::new(&display, scene);
        // renderer.render(&display);
    }
}
<|endoftext|>


<commit_before>extern crate base64;
extern crate file;
extern crate ring;
extern crate rpassword;
extern crate rprompt;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
extern crate username;
extern crate xdg;

use ring::{aead, digest, pbkdf2, rand};
use ring::rand::SecureRandom;
use std::str::FromStr;
use std::path::PathBuf;
use xdg::BaseDirectories;

const SERIALIZATION_API:    usize = 1;
const SERIALIZATION_FIELDS: usize = 4;

#[derive(Debug,PartialEq,Deserialize,Serialize)]
#[serde(tag = "type")]
pub enum Entity {
    Credentials(Credentials),
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
pub struct Credentials {
    pub url:         String,
    pub login:       String,
    pub password:    String,
    pub totp_secret: Option<String>
}

pub struct MnemesisUtils {
    base_dirs: BaseDirectories,
    crypt:     Crypt,
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
struct MnemesisConfig {
    secret: String,
}

impl MnemesisUtils {
    pub fn new() -> MnemesisUtils {
        let base_dirs  = BaseDirectories::with_prefix("mnemesis").expect("Failed getting base directories");
        let passphrase = MnemesisUtils::prompt_for_password("Passphrase: ");
        let config     = MnemesisConfig::load(&base_dirs, &Crypt::new(&passphrase));
        let crypt      = Crypt::new(&config.secret);

        MnemesisUtils {
            base_dirs,
            crypt,
        }
    }

    pub fn prompt_for_input(msg: &str) -> String {
        rprompt::prompt_reply_stdout(msg).expect("Failed reading from stdin")
    }

    pub fn prompt_for_password(msg: &str) -> String {
        rpassword::prompt_password_stdout(msg).expect("Failed reading password")
    }

    fn credentials_directory(&self, path: &str) -> PathBuf {
        self.base_dirs.place_data_file(path).expect("Failed computing credentials directory")
    }

    pub fn read_entities(&self, path: &str) -> Vec<Entity> {
        let full_path = self.credentials_directory(path);

        if full_path.exists() {
            if full_path.is_file() {
                let encrypted_data = file::get_text(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path))).expect(&format!("Failed to read {:?}", full_path));
                let decrypted_data = self.crypt.decrypt(encrypted_data);
                serde_json::from_str::<Vec<Entity>>(&decrypted_data).expect(&format!("Found garbage in {:?}", full_path))
            } else {
                panic!("{:?} exists and is not a file", full_path);
            }
        } else {
            Vec::new()
        }
    }

    pub fn write_entities(&self, path: &str, entities: Vec<Entity>) {
        let full_path      = self.credentials_directory(path);
        let decrypted_data = serde_json::to_string(&entities).expect("Failed to serialize entities");
        let encrypted_data = self.crypt.encrypt(decrypted_data, Algorithm::ChaCha20Poly1305);

        file::put(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path)), &encrypted_data).expect(&format!("Failed to write {:?}", full_path));
    }
}

impl MnemesisConfig {
    pub fn load(base_dirs: &BaseDirectories, crypt: &Crypt) -> MnemesisConfig {
        base_dirs.find_config_file("mnemesis-config.json").and_then(|conf_file| {
            conf_file.to_str().and_then(|conf_file| file::get_text(conf_file).ok()).map(|encrypted_conf| crypt.decrypt(encrypted_conf))
        }).and_then(|conf| {
            serde_json::from_str::<MnemesisConfig>(&conf).ok()
        }).unwrap_or_else(|| {
            let config = MnemesisConfig {
                secret: crypt.generate_passphrase(),
            };
            let decrypted_data = serde_json::to_string(&config).expect("Failed to serialize config");
            let encrypted_data = crypt.encrypt(decrypted_data, Algorithm::ChaCha20Poly1305);
            let full_path      = base_dirs.place_config_file("mnemesis-config.json").expect("Failed to create config file");

            file::put(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path)), &encrypted_data).expect(&format!("Failed to write {:?}", full_path));
            config
        })
    }
}

struct Crypt {
    key:  [u8; 32],
    rand: rand::SystemRandom,
}

#[derive(Clone, Debug, PartialEq)]
enum Algorithm {
    ChaCha20Poly1305,
}

impl Algorithm {
    fn aead_algorithm(&self) -> &'static aead::Algorithm {
        match *self {
            Algorithm::ChaCha20Poly1305 => &aead::CHACHA20_POLY1305
        }
    }
}

impl ToString for Algorithm {
    fn to_string(&self) -> String {
        format!("{:?}", self)
    }
}

impl FromStr for Algorithm {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ChaCha20Poly1305" => Ok(Algorithm::ChaCha20Poly1305),
            _                  => Err(format!("Unknown algorithm: {}", s)),
        }
    }
}

impl Crypt {
    fn new(passphrase: &str) -> Crypt {
        let key = Self::derive(passphrase.as_bytes());

        Crypt {
            key,
            rand: rand::SystemRandom::new(),
        }
    }

    fn derive(bytes: &[u8]) -> [u8; 32] {
        let salt    = username::get_user_name().expect("Failed to query username");
        let mut key = [0; 32];
        pbkdf2::derive(&digest::SHA512, 100, salt.as_bytes(), bytes, &mut key);
        key
    }

    fn random(&self, bytes: usize) -> Vec<u8> {
        let mut rand = vec![0; bytes];
        self.rand.fill(&mut rand).expect("Failed generating random bytes");
        rand
    }

    fn nonce(&self) -> Vec<u8> {
        self.random(12)
    }

    fn encrypt(&self, data: String, algo: Algorithm) -> String {
        let algorithm                = algo.aead_algorithm();
        let sealing_key              = aead::SealingKey::new(&algorithm, &self.key).expect("Failed creating sealing key");
        let nonce                    = self.nonce();
        let additional_data: [u8; 0] = [];
        let mut data                 = data.into_bytes();

        for _ in 0..algorithm.tag_len() {
            data.push(0);
        }

        let encrypted_data = aead::seal_in_place(&sealing_key, &nonce, &additional_data, &mut data, algorithm.tag_len()).map(|len| data[..len].to_vec()).expect("Failed sealing data");

        format!("{}:{}:{}:{}", SERIALIZATION_API, algo.to_string(), base64::encode(&nonce), base64::encode(&encrypted_data))
    }

    fn decrypt(&self, data: String) -> String {
        let components: Vec<_> = data.splitn(SERIALIZATION_FIELDS, ':').collect();

        assert_eq!(components.len(),      SERIALIZATION_FIELDS);
        assert_eq!(components[0].parse(), Ok(SERIALIZATION_API));

        let algorithm                = components[1].parse::<Algorithm>().expect("Unknown algorithm").aead_algorithm();
        let opening_key              = aead::OpeningKey::new(&algorithm, &self.key).expect("Failed creating opening key");
        let nonce                    = base64::decode(components[2]).expect("Failed to decode nonce");
        let mut data                 = base64::decode(components[3]).expect("Failed to decode data");
        let additional_data: [u8; 0] = [];
        let decrypted_data           = aead::open_in_place(&opening_key, &nonce, &additional_data, 0, &mut data).expect("Failed opening data");

        String::from_utf8(decrypted_data.to_vec()).expect("Failed decoding data")
    }

    fn generate_passphrase(&self) -> String {
        base64::encode(&Self::derive(self.random(64).as_ref()))
    }
}

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

    #[test]
    fn test_encrypt_decrypt() {
        let pass  = "This is a nice passphrase.";
        let c     = Crypt::new(pass);
        let data  = "Very secret data";
        let e     = c.encrypt(data.to_string(), Algorithm::ChaCha20Poly1305);
        let e2    = e.clone();

        assert_eq!(c.decrypt(e), data);
        assert_eq!(Crypt::new(pass).decrypt(e2), data);
    }
}
<commit_msg>implement Display for Algorithm<commit_after>extern crate base64;
extern crate file;
extern crate ring;
extern crate rpassword;
extern crate rprompt;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
extern crate username;
extern crate xdg;

use ring::{aead, digest, pbkdf2, rand};
use ring::rand::SecureRandom;
use std::fmt::{self, Display};
use std::str::FromStr;
use std::path::PathBuf;
use xdg::BaseDirectories;

const SERIALIZATION_API:    usize = 1;
const SERIALIZATION_FIELDS: usize = 4;

#[derive(Debug,PartialEq,Deserialize,Serialize)]
#[serde(tag = "type")]
pub enum Entity {
    Credentials(Credentials),
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
pub struct Credentials {
    pub url:         String,
    pub login:       String,
    pub password:    String,
    pub totp_secret: Option<String>
}

pub struct MnemesisUtils {
    base_dirs: BaseDirectories,
    crypt:     Crypt,
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
struct MnemesisConfig {
    secret: String,
}

impl MnemesisUtils {
    pub fn new() -> MnemesisUtils {
        let base_dirs  = BaseDirectories::with_prefix("mnemesis").expect("Failed getting base directories");
        let passphrase = MnemesisUtils::prompt_for_password("Passphrase: ");
        let config     = MnemesisConfig::load(&base_dirs, &Crypt::new(&passphrase));
        let crypt      = Crypt::new(&config.secret);

        MnemesisUtils {
            base_dirs,
            crypt,
        }
    }

    pub fn prompt_for_input(msg: &str) -> String {
        rprompt::prompt_reply_stdout(msg).expect("Failed reading from stdin")
    }

    pub fn prompt_for_password(msg: &str) -> String {
        rpassword::prompt_password_stdout(msg).expect("Failed reading password")
    }

    fn credentials_directory(&self, path: &str) -> PathBuf {
        self.base_dirs.place_data_file(path).expect("Failed computing credentials directory")
    }

    pub fn read_entities(&self, path: &str) -> Vec<Entity> {
        let full_path = self.credentials_directory(path);

        if full_path.exists() {
            if full_path.is_file() {
                let encrypted_data = file::get_text(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path))).expect(&format!("Failed to read {:?}", full_path));
                let decrypted_data = self.crypt.decrypt(encrypted_data);
                serde_json::from_str::<Vec<Entity>>(&decrypted_data).expect(&format!("Found garbage in {:?}", full_path))
            } else {
                panic!("{:?} exists and is not a file", full_path);
            }
        } else {
            Vec::new()
        }
    }

    pub fn write_entities(&self, path: &str, entities: Vec<Entity>) {
        let full_path      = self.credentials_directory(path);
        let decrypted_data = serde_json::to_string(&entities).expect("Failed to serialize entities");
        let encrypted_data = self.crypt.encrypt(decrypted_data, Algorithm::ChaCha20Poly1305);

        file::put(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path)), &encrypted_data).expect(&format!("Failed to write {:?}", full_path));
    }
}

impl MnemesisConfig {
    pub fn load(base_dirs: &BaseDirectories, crypt: &Crypt) -> MnemesisConfig {
        base_dirs.find_config_file("mnemesis-config.json").and_then(|conf_file| {
            conf_file.to_str().and_then(|conf_file| file::get_text(conf_file).ok()).map(|encrypted_conf| crypt.decrypt(encrypted_conf))
        }).and_then(|conf| {
            serde_json::from_str::<MnemesisConfig>(&conf).ok()
        }).unwrap_or_else(|| {
            let config = MnemesisConfig {
                secret: crypt.generate_passphrase(),
            };
            let decrypted_data = serde_json::to_string(&config).expect("Failed to serialize config");
            let encrypted_data = crypt.encrypt(decrypted_data, Algorithm::ChaCha20Poly1305);
            let full_path      = base_dirs.place_config_file("mnemesis-config.json").expect("Failed to create config file");

            file::put(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path)), &encrypted_data).expect(&format!("Failed to write {:?}", full_path));
            config
        })
    }
}

struct Crypt {
    key:  [u8; 32],
    rand: rand::SystemRandom,
}

#[derive(Clone, Debug, PartialEq)]
enum Algorithm {
    ChaCha20Poly1305,
}

impl Algorithm {
    fn aead_algorithm(&self) -> &'static aead::Algorithm {
        match *self {
            Algorithm::ChaCha20Poly1305 => &aead::CHACHA20_POLY1305
        }
    }
}

impl Display for Algorithm {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{:?}", self)
    }
}

impl FromStr for Algorithm {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ChaCha20Poly1305" => Ok(Algorithm::ChaCha20Poly1305),
            _                  => Err(format!("Unknown algorithm: {}", s)),
        }
    }
}

impl Crypt {
    fn new(passphrase: &str) -> Crypt {
        let key = Self::derive(passphrase.as_bytes());

        Crypt {
            key,
            rand: rand::SystemRandom::new(),
        }
    }

    fn derive(bytes: &[u8]) -> [u8; 32] {
        let salt    = username::get_user_name().expect("Failed to query username");
        let mut key = [0; 32];
        pbkdf2::derive(&digest::SHA512, 100, salt.as_bytes(), bytes, &mut key);
        key
    }

    fn random(&self, bytes: usize) -> Vec<u8> {
        let mut rand = vec![0; bytes];
        self.rand.fill(&mut rand).expect("Failed generating random bytes");
        rand
    }

    fn nonce(&self) -> Vec<u8> {
        self.random(12)
    }

    fn encrypt(&self, data: String, algo: Algorithm) -> String {
        let algorithm                = algo.aead_algorithm();
        let sealing_key              = aead::SealingKey::new(&algorithm, &self.key).expect("Failed creating sealing key");
        let nonce                    = self.nonce();
        let additional_data: [u8; 0] = [];
        let mut data                 = data.into_bytes();

        for _ in 0..algorithm.tag_len() {
            data.push(0);
        }

        let encrypted_data = aead::seal_in_place(&sealing_key, &nonce, &additional_data, &mut data, algorithm.tag_len()).map(|len| data[..len].to_vec()).expect("Failed sealing data");

        format!("{}:{}:{}:{}", SERIALIZATION_API, algo, base64::encode(&nonce), base64::encode(&encrypted_data))
    }

    fn decrypt(&self, data: String) -> String {
        let components: Vec<_> = data.splitn(SERIALIZATION_FIELDS, ':').collect();

        assert_eq!(components.len(),      SERIALIZATION_FIELDS);
        assert_eq!(components[0].parse(), Ok(SERIALIZATION_API));

        let algorithm                = components[1].parse::<Algorithm>().expect("Unknown algorithm").aead_algorithm();
        let opening_key              = aead::OpeningKey::new(&algorithm, &self.key).expect("Failed creating opening key");
        let nonce                    = base64::decode(components[2]).expect("Failed to decode nonce");
        let mut data                 = base64::decode(components[3]).expect("Failed to decode data");
        let additional_data: [u8; 0] = [];
        let decrypted_data           = aead::open_in_place(&opening_key, &nonce, &additional_data, 0, &mut data).expect("Failed opening data");

        String::from_utf8(decrypted_data.to_vec()).expect("Failed decoding data")
    }

    fn generate_passphrase(&self) -> String {
        base64::encode(&Self::derive(self.random(64).as_ref()))
    }
}

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

    #[test]
    fn test_encrypt_decrypt() {
        let pass  = "This is a nice passphrase.";
        let c     = Crypt::new(pass);
        let data  = "Very secret data";
        let e     = c.encrypt(data.to_string(), Algorithm::ChaCha20Poly1305);
        let e2    = e.clone();

        assert_eq!(c.decrypt(e), data);
        assert_eq!(Crypt::new(pass).decrypt(e2), data);
    }
}
<|endoftext|>


<commit_before>#![crate_type = "lib"]
#![feature(unsafe_destructor)]

use std::sync::atomics::{AtomicBool, SeqCst};
use std::ty::Unsafe;
use std::rt::local::Local;
use std::rt::task::Task;

pub struct Spinlock<T>
{
    lock: AtomicBool,
    data: Unsafe<T>,
}

pub struct SpinlockGuard<'a, T>
{
    lock: &'a AtomicBool,
    data: &'a mut T,
}

impl<T: Send> Spinlock<T>
{
    pub fn new(user_data: T) -> Spinlock<T>
    {
        Spinlock
        {
            lock: AtomicBool::new(false),
            data: Unsafe::new(user_data),
        }
    }

    fn _lock(&self)
    {
        while self.lock.compare_and_swap(false, true, SeqCst) != false
        {
            if Local::exists(None::<Task>)
            {
                std::task::deschedule();
            }
        }
    }

    pub fn lock(&self) -> SpinlockGuard<T>
    {
        self._lock();
        SpinlockGuard
        {
            lock: &self.lock,
            data: unsafe { &mut *self.data.get() },
        }
    }
}

impl<'a, T: Send> Deref<T> for SpinlockGuard<'a, T>
{
    fn deref<'a>(&'a self) -> &'a T { &*self.data }
}

impl<'a, T: Send> DerefMut<T> for SpinlockGuard<'a, T>
{
    fn deref_mut<'a>(&'a mut self) -> &'a mut T { &mut *self.data }
}

#[unsafe_destructor]
impl<'a, T: Send> Drop for SpinlockGuard<'a, T>
{
    fn drop(&mut self)
    {
        self.lock.store(false, SeqCst);
    }
}
<commit_msg>Added lots of comments<commit_after>#![crate_name = "spinlock"]
#![experimental]
#![crate_type = "lib"]
#![feature(unsafe_destructor)]

//! This crate provides a thread-safe data container.
//!
//! # Description
//!
//! This structure behaves a lot like a normal Mutex. There are some differences:
//!
//! - It may be used outside the runtime.
//!   - A normal mutex will fail when used without the runtime, this will just lock
//!   - When the runtime is present, it will call the deschedule function when appropriate
//! - No lock poisoning. When a fail occurs when the lock is held, no guarantees are made
//!
//! When calling rust functions from bare threads, such as C `pthread`s, this lock will be very
//! helpfull. In other cases however, you are encouraged to use the locks from the standard
//! library.
//!
//! # Simple example
//!
//! ```
//! use spinlock::Spinlock;
//! let spinlock = Spinlock::new(0u);
//!
//! // Modify the data
//! {
//!     let mut data = spinlock.lock();
//!     *data = 2;
//! }
//!
//! // Read the data
//! let answer =
//! {
//!     let data = spinlock.lock();
//!     *data
//! };
//!
//! assert_eq!(answer, 2u);
//! ```
//!
//! # Thread-safety example
//!
//! ```
//! use spinlock::Spinlock;
//! use std::sync::{Arc, Barrier};
//!
//! let numthreads = 1000;
//! let spinlock = Arc::new(Spinlock::new(0u));
//!
//! // We use a barrier to ensure the readout happens after all writing
//! let barrier = Arc::new(Barrier::new(numthreads + 1));
//!
//! for _ in range(0, numthreads)
//! {
//!     let my_barrier = barrier.clone();
//!     let my_lock = spinlock.clone();
//!     spawn(proc()
//!     {
//!         let mut guard = my_lock.lock();
//!         *guard += 1;
//!
//!         // Release the lock to prevent a deadlock
//!         drop(guard);
//!         my_barrier.wait();
//!     });
//! }
//!
//! barrier.wait();
//!
//! let answer = { *spinlock.lock() };
//! assert_eq!(answer, numthreads);
//! ```

use std::sync::atomics::{AtomicBool, SeqCst};
use std::ty::Unsafe;
use std::rt::local::Local;
use std::rt::task::Task;

/// A wrapper for the data giving access in a thread-safe manner
pub struct Spinlock<T>
{
    lock: AtomicBool,
    data: Unsafe<T>,
}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
#[experimental]
pub struct SpinlockGuard<'a, T>
{
    lock: &'a AtomicBool,
    data: &'a mut T,
}

impl<T: Send> Spinlock<T>
{
    /// Creates a new spinlock wrapping the supplied data.
    #[unstable]
    pub fn new(user_data: T) -> Spinlock<T>
    {
        Spinlock
        {
            lock: AtomicBool::new(false),
            data: Unsafe::new(user_data),
        }
    }

    fn _lock(&self)
    {
        while self.lock.compare_and_swap(false, true, SeqCst) != false
        {
            if Local::exists(None::<Task>)
            {
                std::task::deschedule();
            }
        }
    }

    /// Locks the spinlock and returns a guard.
    ///
    /// The returned value may be dereferenced for data access
    /// and the lock will be dropped when the guard falls out of scope.
    ///
    /// ```
    /// let mylock = spinlock::Spinlock::new(0u);
    /// {
    ///     let mut data = mylock.lock();
    ///     // The lock is now locked and the data can be accessed
    ///     *data += 1;
    /// }
    /// // The lock is dropped
    /// ```
    pub fn lock(&self) -> SpinlockGuard<T>
    {
        self._lock();
        SpinlockGuard
        {
            lock: &self.lock,
            data: unsafe { &mut *self.data.get() },
        }
    }
}

impl<'a, T: Send> Deref<T> for SpinlockGuard<'a, T>
{
    fn deref<'a>(&'a self) -> &'a T { &*self.data }
}

impl<'a, T: Send> DerefMut<T> for SpinlockGuard<'a, T>
{
    fn deref_mut<'a>(&'a mut self) -> &'a mut T { &mut *self.data }
}

#[unsafe_destructor]
impl<'a, T: Send> Drop for SpinlockGuard<'a, T>
{
    /// The dropping of the SpinlockGuard will release the lock it was created from.
    fn drop(&mut self)
    {
        self.lock.store(false, SeqCst);
    }
}
<|endoftext|>