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

use cairo::{self, PatternTrait, Surface};
use gdk_pixbuf;
use gdk_sys;
use glib::object::IsA;
use glib::translate::*;
use libc::{c_char, c_int};
use std::ptr;
use Cursor;
use Visual;
use Window;

use {WindowHints, WindowType, WindowTypeHint, WindowWindowClass};

pub struct WindowAttr {
    pub title: Option<String>,
    pub event_mask: i32,
    pub x: Option<i32>,
    pub y: Option<i32>,
    pub width: i32,
    pub height: i32,
    pub wclass: WindowWindowClass,
    pub visual: Option<Visual>,
    pub window_type: WindowType,
    pub cursor: Option<Cursor>,
    pub override_redirect: bool,
    pub type_hint: Option<WindowTypeHint>,
}

impl Default for WindowAttr {
    fn default() -> WindowAttr {
        skip_assert_initialized!();
        WindowAttr {
            title: None,
            event_mask: 0,
            x: None,
            y: None,
            width: 400,
            height: 300,
            wclass: WindowWindowClass::InputOutput,
            visual: None,
            window_type: WindowType::Toplevel,
            cursor: None,
            override_redirect: false,
            type_hint: None,
        }
    }
}

impl WindowAttr {
    fn get_mask(&self) -> u32 {
        let mut mask: gdk_sys::GdkWindowAttributesType = 0;
        if self.title.is_some() {
            mask |= gdk_sys::GDK_WA_TITLE;
        }
        if self.x.is_some() {
            mask |= gdk_sys::GDK_WA_X;
        }
        if self.y.is_some() {
            mask |= gdk_sys::GDK_WA_Y;
        }
        if self.cursor.is_some() {
            mask |= gdk_sys::GDK_WA_CURSOR;
        }
        if self.visual.is_some() {
            mask |= gdk_sys::GDK_WA_VISUAL;
        }
        if self.override_redirect {
            mask |= gdk_sys::GDK_WA_NOREDIR;
        }
        if self.type_hint.is_some() {
            mask |= gdk_sys::GDK_WA_TYPE_HINT;
        }
        mask
    }
}

#[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
impl<'a> ToGlibPtr<'a, *mut gdk_sys::GdkWindowAttr> for WindowAttr {
    type Storage = (
        Box<gdk_sys::GdkWindowAttr>,
        Stash<'a, *mut gdk_sys::GdkVisual, Option<Visual>>,
        Stash<'a, *mut gdk_sys::GdkCursor, Option<Cursor>>,
        Stash<'a, *const c_char, Option<String>>,
    );

    fn to_glib_none(&'a self) -> Stash<'a, *mut gdk_sys::GdkWindowAttr, Self> {
        let title = self.title.to_glib_none();
        let visual = self.visual.to_glib_none();
        let cursor = self.cursor.to_glib_none();

        let mut attrs = Box::new(gdk_sys::GdkWindowAttr {
            title: title.0 as *mut c_char,
            event_mask: self.event_mask,
            x: self.x.unwrap_or(0),
            y: self.y.unwrap_or(0),
            width: self.width,
            height: self.height,
            wclass: self.wclass.to_glib(),
            visual: visual.0,
            window_type: self.window_type.to_glib(),
            cursor: cursor.0,
            wmclass_name: ptr::null_mut(),
            wmclass_class: ptr::null_mut(),
            override_redirect: self.override_redirect.to_glib(),
            type_hint: self.type_hint.unwrap_or(WindowTypeHint::Normal).to_glib(),
        });

        Stash(&mut *attrs, (attrs, visual, cursor, title))
    }
}

impl Window {
    pub fn new(parent: Option<&Window>, attributes: &WindowAttr) -> Window {
        assert_initialized_main_thread!();
        unsafe {
            from_glib_full(gdk_sys::gdk_window_new(
                parent.to_glib_none().0,
                attributes.to_glib_none().0,
                attributes.get_mask() as c_int,
            ))
        }
    }

    pub fn create_similar_surface(
        &self,
        content: cairo::Content,
        width: i32,
        height: i32,
    ) -> Option<Surface> {
        unsafe {
            from_glib_full(gdk_sys::gdk_window_create_similar_surface(
                self.to_glib_none().0,
                content.into(),
                width,
                height,
            ))
        }
    }
}

pub trait WindowExtManual: 'static {
    unsafe fn set_user_data<T>(&self, user_data: &mut T);

    #[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref))]
    unsafe fn get_user_data<T>(&self) -> &mut T;

    fn set_geometry_hints(&self, geometry: &gdk_sys::GdkGeometry, geom_mask: WindowHints);

    fn get_default_root_window() -> Window;

    fn offscreen_window_set_embedder(&self, embedder: &Window);

    fn offscreen_window_get_embedder(&self) -> Option<Window>;

    fn offscreen_window_get_surface(&self) -> Option<Surface>;

    fn get_pixbuf(
        &self,
        src_x: i32,
        src_y: i32,
        width: i32,
        height: i32,
    ) -> Option<gdk_pixbuf::Pixbuf>;

    fn get_background_pattern(&self) -> Option<cairo::Pattern>;

    fn set_background_pattern(&self, pattern: Option<&cairo::Pattern>);
}

impl<O: IsA<Window>> WindowExtManual for O {
    unsafe fn set_user_data<T>(&self, user_data: &mut T) {
        gdk_sys::gdk_window_set_user_data(
            self.as_ref().to_glib_none().0,
            user_data as *mut T as *mut _,
        )
    }

    unsafe fn get_user_data<T>(&self) -> &mut T {
        let mut pointer = ::std::ptr::null_mut();
        gdk_sys::gdk_window_get_user_data(self.as_ref().to_glib_none().0, &mut pointer);
        &mut *(pointer as *mut T)
    }

    fn set_geometry_hints(&self, geometry: &gdk_sys::GdkGeometry, geom_mask: WindowHints) {
        unsafe {
            gdk_sys::gdk_window_set_geometry_hints(
                self.as_ref().to_glib_none().0,
                geometry,
                geom_mask.to_glib(),
            )
        }
    }

    fn get_default_root_window() -> Window {
        assert_initialized_main_thread!();
        unsafe { from_glib_none(gdk_sys::gdk_get_default_root_window()) }
    }

    fn offscreen_window_set_embedder(&self, embedder: &Window) {
        unsafe {
            gdk_sys::gdk_offscreen_window_set_embedder(
                self.as_ref().to_glib_none().0,
                embedder.to_glib_none().0,
            )
        }
    }

    fn offscreen_window_get_embedder(&self) -> Option<Window> {
        unsafe {
            from_glib_none(gdk_sys::gdk_offscreen_window_get_embedder(
                self.as_ref().to_glib_none().0,
            ))
        }
    }

    fn offscreen_window_get_surface(&self) -> Option<Surface> {
        skip_assert_initialized!();
        unsafe {
            from_glib_none(gdk_sys::gdk_offscreen_window_get_surface(
                self.as_ref().to_glib_none().0,
            ))
        }
    }

    fn get_pixbuf(
        &self,
        src_x: i32,
        src_y: i32,
        width: i32,
        height: i32,
    ) -> Option<gdk_pixbuf::Pixbuf> {
        skip_assert_initialized!();
        unsafe {
            from_glib_full(gdk_sys::gdk_pixbuf_get_from_window(
                self.as_ref().to_glib_none().0,
                src_x,
                src_y,
                width,
                height,
            ))
        }
    }

    fn get_background_pattern(&self) -> Option<cairo::Pattern> {
        unsafe {
            let ret = gdk_sys::gdk_window_get_background_pattern(self.as_ref().to_glib_none().0);
            if ret.is_null() {
                None
            } else {
                Some(cairo::Pattern::from_raw_none(ret))
            }
        }
    }

    fn set_background_pattern(&self, pattern: Option<&cairo::Pattern>) {
        unsafe {
            let ptr = if let Some(pattern) = pattern {
                pattern.as_ptr()
            } else {
                ::std::ptr::null_mut()
            };
            gdk_sys::gdk_window_set_background_pattern(self.as_ref().to_glib_none().0, ptr);
        }
    }
}
<commit_msg>Update to last cairo version<commit_after>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use cairo::{self, Surface};
use gdk_pixbuf;
use gdk_sys;
use glib::object::IsA;
use glib::translate::*;
use libc::{c_char, c_int};
use std::ptr;
use Cursor;
use Visual;
use Window;

use {WindowHints, WindowType, WindowTypeHint, WindowWindowClass};

pub struct WindowAttr {
    pub title: Option<String>,
    pub event_mask: i32,
    pub x: Option<i32>,
    pub y: Option<i32>,
    pub width: i32,
    pub height: i32,
    pub wclass: WindowWindowClass,
    pub visual: Option<Visual>,
    pub window_type: WindowType,
    pub cursor: Option<Cursor>,
    pub override_redirect: bool,
    pub type_hint: Option<WindowTypeHint>,
}

impl Default for WindowAttr {
    fn default() -> WindowAttr {
        skip_assert_initialized!();
        WindowAttr {
            title: None,
            event_mask: 0,
            x: None,
            y: None,
            width: 400,
            height: 300,
            wclass: WindowWindowClass::InputOutput,
            visual: None,
            window_type: WindowType::Toplevel,
            cursor: None,
            override_redirect: false,
            type_hint: None,
        }
    }
}

impl WindowAttr {
    fn get_mask(&self) -> u32 {
        let mut mask: gdk_sys::GdkWindowAttributesType = 0;
        if self.title.is_some() {
            mask |= gdk_sys::GDK_WA_TITLE;
        }
        if self.x.is_some() {
            mask |= gdk_sys::GDK_WA_X;
        }
        if self.y.is_some() {
            mask |= gdk_sys::GDK_WA_Y;
        }
        if self.cursor.is_some() {
            mask |= gdk_sys::GDK_WA_CURSOR;
        }
        if self.visual.is_some() {
            mask |= gdk_sys::GDK_WA_VISUAL;
        }
        if self.override_redirect {
            mask |= gdk_sys::GDK_WA_NOREDIR;
        }
        if self.type_hint.is_some() {
            mask |= gdk_sys::GDK_WA_TYPE_HINT;
        }
        mask
    }
}

#[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
impl<'a> ToGlibPtr<'a, *mut gdk_sys::GdkWindowAttr> for WindowAttr {
    type Storage = (
        Box<gdk_sys::GdkWindowAttr>,
        Stash<'a, *mut gdk_sys::GdkVisual, Option<Visual>>,
        Stash<'a, *mut gdk_sys::GdkCursor, Option<Cursor>>,
        Stash<'a, *const c_char, Option<String>>,
    );

    fn to_glib_none(&'a self) -> Stash<'a, *mut gdk_sys::GdkWindowAttr, Self> {
        let title = self.title.to_glib_none();
        let visual = self.visual.to_glib_none();
        let cursor = self.cursor.to_glib_none();

        let mut attrs = Box::new(gdk_sys::GdkWindowAttr {
            title: title.0 as *mut c_char,
            event_mask: self.event_mask,
            x: self.x.unwrap_or(0),
            y: self.y.unwrap_or(0),
            width: self.width,
            height: self.height,
            wclass: self.wclass.to_glib(),
            visual: visual.0,
            window_type: self.window_type.to_glib(),
            cursor: cursor.0,
            wmclass_name: ptr::null_mut(),
            wmclass_class: ptr::null_mut(),
            override_redirect: self.override_redirect.to_glib(),
            type_hint: self.type_hint.unwrap_or(WindowTypeHint::Normal).to_glib(),
        });

        Stash(&mut *attrs, (attrs, visual, cursor, title))
    }
}

impl Window {
    pub fn new(parent: Option<&Window>, attributes: &WindowAttr) -> Window {
        assert_initialized_main_thread!();
        unsafe {
            from_glib_full(gdk_sys::gdk_window_new(
                parent.to_glib_none().0,
                attributes.to_glib_none().0,
                attributes.get_mask() as c_int,
            ))
        }
    }

    pub fn create_similar_surface(
        &self,
        content: cairo::Content,
        width: i32,
        height: i32,
    ) -> Option<Surface> {
        unsafe {
            from_glib_full(gdk_sys::gdk_window_create_similar_surface(
                self.to_glib_none().0,
                content.into(),
                width,
                height,
            ))
        }
    }
}

pub trait WindowExtManual: 'static {
    unsafe fn set_user_data<T>(&self, user_data: &mut T);

    #[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref))]
    unsafe fn get_user_data<T>(&self) -> &mut T;

    fn set_geometry_hints(&self, geometry: &gdk_sys::GdkGeometry, geom_mask: WindowHints);

    fn get_default_root_window() -> Window;

    fn offscreen_window_set_embedder(&self, embedder: &Window);

    fn offscreen_window_get_embedder(&self) -> Option<Window>;

    fn offscreen_window_get_surface(&self) -> Option<Surface>;

    fn get_pixbuf(
        &self,
        src_x: i32,
        src_y: i32,
        width: i32,
        height: i32,
    ) -> Option<gdk_pixbuf::Pixbuf>;

    fn get_background_pattern(&self) -> Option<cairo::Pattern>;

    fn set_background_pattern(&self, pattern: Option<&cairo::Pattern>);
}

impl<O: IsA<Window>> WindowExtManual for O {
    unsafe fn set_user_data<T>(&self, user_data: &mut T) {
        gdk_sys::gdk_window_set_user_data(
            self.as_ref().to_glib_none().0,
            user_data as *mut T as *mut _,
        )
    }

    unsafe fn get_user_data<T>(&self) -> &mut T {
        let mut pointer = ::std::ptr::null_mut();
        gdk_sys::gdk_window_get_user_data(self.as_ref().to_glib_none().0, &mut pointer);
        &mut *(pointer as *mut T)
    }

    fn set_geometry_hints(&self, geometry: &gdk_sys::GdkGeometry, geom_mask: WindowHints) {
        unsafe {
            gdk_sys::gdk_window_set_geometry_hints(
                self.as_ref().to_glib_none().0,
                geometry,
                geom_mask.to_glib(),
            )
        }
    }

    fn get_default_root_window() -> Window {
        assert_initialized_main_thread!();
        unsafe { from_glib_none(gdk_sys::gdk_get_default_root_window()) }
    }

    fn offscreen_window_set_embedder(&self, embedder: &Window) {
        unsafe {
            gdk_sys::gdk_offscreen_window_set_embedder(
                self.as_ref().to_glib_none().0,
                embedder.to_glib_none().0,
            )
        }
    }

    fn offscreen_window_get_embedder(&self) -> Option<Window> {
        unsafe {
            from_glib_none(gdk_sys::gdk_offscreen_window_get_embedder(
                self.as_ref().to_glib_none().0,
            ))
        }
    }

    fn offscreen_window_get_surface(&self) -> Option<Surface> {
        skip_assert_initialized!();
        unsafe {
            from_glib_none(gdk_sys::gdk_offscreen_window_get_surface(
                self.as_ref().to_glib_none().0,
            ))
        }
    }

    fn get_pixbuf(
        &self,
        src_x: i32,
        src_y: i32,
        width: i32,
        height: i32,
    ) -> Option<gdk_pixbuf::Pixbuf> {
        skip_assert_initialized!();
        unsafe {
            from_glib_full(gdk_sys::gdk_pixbuf_get_from_window(
                self.as_ref().to_glib_none().0,
                src_x,
                src_y,
                width,
                height,
            ))
        }
    }

    fn get_background_pattern(&self) -> Option<cairo::Pattern> {
        unsafe {
            let ret = gdk_sys::gdk_window_get_background_pattern(self.as_ref().to_glib_none().0);
            if ret.is_null() {
                None
            } else {
                Some(cairo::Pattern::from_raw_none(ret))
            }
        }
    }

    fn set_background_pattern(&self, pattern: Option<&cairo::Pattern>) {
        unsafe {
            let ptr = if let Some(pattern) = pattern {
                pattern.to_raw_none()
            } else {
                ::std::ptr::null_mut()
            };
            gdk_sys::gdk_window_set_background_pattern(self.as_ref().to_glib_none().0, ptr);
        }
    }
}
<|endoftext|>


<commit_before>//! Utility functions to write nifti images.

use std::fs::File;
use std::io::BufWriter;
use std::mem;
use std::ops::{Div, Sub};
use std::path::Path;

use byteorder::{LittleEndian, WriteBytesExt};
use flate2::write::GzEncoder;
use flate2::Compression;
use ndarray::{Array, ArrayBase, Axis, Data, Dimension, RemoveAxis, ScalarOperand};
use num_traits::FromPrimitive;
use safe_transmute::{guarded_transmute_to_bytes_pod_many, PodTransmutable};

use {util::is_gz_file, volume::element::DataElement, NiftiHeader, Result};

// TODO make this configurable. The Nifti standard does not specify a specific field for endianness,
// but it is encoded in `dim[0]`. "if dim[0] is outside range 1..7, then swap".
type B = LittleEndian;

/// Write a nifti file (.nii or .nii.gz) in Little Endian.
///
/// If a `reference` is given, it will be used to fill most of the header's fields. The voxels
/// intensity will be subtracted by `scl_slope` and divided by `scl_inter`. If `reference` is not
/// given, a default `NiftiHeader` will be built and written.
///
/// In all cases, the `dim`, `datatype` and `bitpix` fields will depend only on `data`, not on the
/// header. In other words, the `datatype` defined in `reference` will be ignored.
pub fn write_nifti<P, A, S, D>(
    path: P,
    data: &ArrayBase<S, D>,
    reference: Option<&NiftiHeader>,
) -> Result<()>
where
    P: AsRef<Path>,
    S: Data<Elem = A>,
    A: Copy,
    A: DataElement,
    A: Div<Output = A>,
    A: FromPrimitive,
    A: PodTransmutable,
    A: ScalarOperand,
    A: Sub<Output = A>,
    D: Dimension + RemoveAxis,
{
    let mut dim = [1; 8];
    dim[0] = data.ndim() as u16;
    for (i, s) in data.shape().iter().enumerate() {
        dim[i + 1] = *s as u16;
    }

    // If no reference header is given, use the default.
    let reference = match reference {
        Some(r) => r.clone(),
        None => NiftiHeader::default(),
    };
    let header = NiftiHeader {
        dim,
        datatype: A::DATA_TYPE as i16,
        bitpix: (mem::size_of::<A>() * 8) as i16,
        // All other fields are copied from reference header
        ..reference
    };

    let f = File::create(&path)?;
    let mut writer = BufWriter::new(f);
    if is_gz_file(&path) {
        let mut e = GzEncoder::new(writer, Compression::fast());
        write_header(&mut e, &header)?;
        write_data(&mut e, header, data)?;
        let _ = e.finish()?; // Must use result
    } else {
        write_header(&mut writer, &header)?;
        write_data(&mut writer, header, data)?;
    }
    Ok(())
}

fn write_header<W>(writer: &mut W, header: &NiftiHeader) -> Result<()>
where
    W: WriteBytesExt,
{
    writer.write_i32::<B>(header.sizeof_hdr)?;
    writer.write_all(&header.data_type)?;
    writer.write_all(&header.db_name)?;
    writer.write_i32::<B>(header.extents)?;
    writer.write_i16::<B>(header.session_error)?;
    writer.write_u8(header.regular)?;
    writer.write_u8(header.dim_info)?;
    for s in &header.dim {
        writer.write_u16::<B>(*s)?;
    }
    writer.write_f32::<B>(header.intent_p1)?;
    writer.write_f32::<B>(header.intent_p2)?;
    writer.write_f32::<B>(header.intent_p3)?;
    writer.write_i16::<B>(header.intent_code)?;
    writer.write_i16::<B>(header.datatype)?;
    writer.write_i16::<B>(header.bitpix)?;
    writer.write_i16::<B>(header.slice_start)?;
    for f in &header.pixdim {
        writer.write_f32::<B>(*f)?;
    }
    writer.write_f32::<B>(header.vox_offset)?;
    writer.write_f32::<B>(header.scl_slope)?;
    writer.write_f32::<B>(header.scl_inter)?;
    writer.write_i16::<B>(header.slice_end)?;
    writer.write_u8(header.slice_code)?;
    writer.write_u8(header.xyzt_units)?;
    writer.write_f32::<B>(header.cal_max)?;
    writer.write_f32::<B>(header.cal_min)?;
    writer.write_f32::<B>(header.slice_duration)?;
    writer.write_f32::<B>(header.toffset)?;
    writer.write_i32::<B>(header.glmax)?;
    writer.write_i32::<B>(header.glmin)?;

    writer.write_all(&header.descrip)?;
    writer.write_all(&header.aux_file)?;
    writer.write_i16::<B>(header.qform_code)?;
    writer.write_i16::<B>(header.sform_code)?;
    for f in &[
        header.quatern_b,
        header.quatern_c,
        header.quatern_d,
        header.quatern_x,
        header.quatern_y,
        header.quatern_z,
    ] {
        writer.write_f32::<B>(*f)?;
    }
    for f in header
        .srow_x
        .iter()
        .chain(&header.srow_y)
        .chain(&header.srow_z)
    {
        writer.write_f32::<B>(*f)?;
    }
    writer.write_all(&header.intent_name)?;
    writer.write_all(&header.magic)?;

    // Empty 4 bytes after the header
    // TODO Support writing extension data.
    writer.write_u32::<B>(0)?;

    Ok(())
}

/// Write the data in 'f' order.
///
/// Like NiBabel, we iterate by "slice" to improve speed and use less memory.
fn write_data<A, S, D, W>(writer: &mut W, header: NiftiHeader, data: &ArrayBase<S, D>) -> Result<()>
where
    S: Data<Elem = A>,
    A: Copy,
    A: DataElement,
    A: Div<Output = A>,
    A: FromPrimitive,
    A: PodTransmutable,
    A: ScalarOperand,
    A: Sub<Output = A>,
    D: Dimension + RemoveAxis,
    W: WriteBytesExt,
{
    // Need the transpose for fortran ordering used in nifti file format.
    let data = data.t();

    let mut write_slice = |data: Array<A, D::Smaller>| -> Result<()> {
        let len = data.len();
        let arr_data = data.into_shape(len).unwrap();
        let slice = arr_data.as_slice().unwrap();
        writer.write_all(guarded_transmute_to_bytes_pod_many(slice))?;
        Ok(())
    };

    // `1.0x + 0.0` would give the same results, but we avoid a lot of divisions
    let slope = if header.scl_slope == 0.0 {
        1.0
    } else {
        header.scl_slope
    };
    if slope != 1.0 || header.scl_inter != 0.0 {
        let slope = A::from_f32(slope).unwrap();
        let inter = A::from_f32(header.scl_inter).unwrap();
        for arr_data in data.axis_iter(Axis(0)) {
            write_slice(arr_data.sub(inter).div(slope))?;
        }
    } else {
        for arr_data in data.axis_iter(Axis(0)) {
            // We need to own the data because of the into_shape() for `c` ordering.
            write_slice(arr_data.to_owned())?;
        }
    }
    Ok(())
}

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

    use super::*;
    use std::ops::{Add, Mul};
    use std::path::PathBuf;

    use self::tempfile::tempdir;
    use ndarray::{Array, Array2, Ix2, IxDyn, ShapeBuilder};

    use {header::MAGIC_CODE_NIP1, object::NiftiObject, InMemNiftiObject, IntoNdArray};

    fn get_random_path(ext: &str) -> PathBuf {
        let dir = tempdir().unwrap();
        let mut path = dir.into_path();
        if !ext.is_empty() {
            path.push(ext);
        }
        path
    }

    pub fn generate_nifti_header(
        dim: [u16; 8],
        scl_slope: f32,
        scl_inter: f32,
        datatype: i16,
    ) -> NiftiHeader {
        NiftiHeader {
            dim,
            datatype,
            bitpix: (mem::size_of::<f32>() * 8) as i16,
            magic: *MAGIC_CODE_NIP1,
            scl_slope,
            scl_inter,
            ..NiftiHeader::default()
        }
    }

    fn read_2d_image<P>(path: P) -> Array2<f32>
    where
        P: AsRef<Path>,
    {
        let nifti_object = InMemNiftiObject::from_file(path).expect("Nifti file is unreadable.");
        let volume = nifti_object.into_volume();
        let dyn_data = volume.into_ndarray().unwrap();
        dyn_data.into_dimensionality::<Ix2>().unwrap()
    }

    fn test_write_read(arr: &Array<f32, IxDyn>, path: &str) {
        let path = get_random_path(path);
        let mut dim = [1; 8];
        dim[0] = arr.ndim() as u16;
        for (i, s) in arr.shape().iter().enumerate() {
            dim[i + 1] = *s as u16;
        }
        let header = generate_nifti_header(dim, 1.0, 0.0, 16);
        write_nifti(&path, &arr, Some(&header)).unwrap();

        let read_nifti = read_2d_image(path);
        assert!(read_nifti.all_close(&arr, 1e-10));
    }

    fn f_order_array() -> Array<f32, IxDyn> {
        let dim = vec![4, 4];
        let vec = (0..16).map(|x| x as f32).collect();
        Array::from_shape_vec(IxDyn(&dim).f(), vec).unwrap()
    }

    fn c_order_array() -> Array<f32, IxDyn> {
        let dim = vec![4, 4];
        let vec = (0..16).map(|x| x as f32).collect();
        Array::from_shape_vec(IxDyn(&dim), vec).unwrap()
    }

    #[test]
    fn test_fortran_writing() {
        // Test .nii
        let arr = f_order_array();
        test_write_read(&arr, "test.nii");
        let mut arr = f_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii");

        // Test .nii.gz
        let arr = f_order_array();
        test_write_read(&arr, "test.nii.gz");
        let mut arr = f_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii.gz");
    }

    #[test]
    fn test_c_writing() {
        // Test .nii
        let arr = c_order_array();
        test_write_read(&arr, "test.nii");

        let mut arr = c_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii");

        // Test .nii.gz
        let arr = c_order_array();
        test_write_read(&arr, "test.nii.gz");

        let mut arr = c_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii.gz");
    }

    #[test]
    fn test_header_slope_inter() {
        let arr = f_order_array();
        let slope = 2.2;
        let inter = 101.1;

        let path = get_random_path("test_slope_inter.nii");
        let mut dim = [1; 8];
        dim[0] = arr.ndim() as u16;
        for (i, s) in arr.shape().iter().enumerate() {
            dim[i + 1] = *s as u16;
        }
        let header = generate_nifti_header(dim, slope, inter, 16);
        let transformed_data = arr.mul(slope).add(inter);
        write_nifti(&path, &transformed_data, Some(&header)).unwrap();

        let read_nifti = read_2d_image(path);
        assert!(read_nifti.all_close(&transformed_data, 1e-10));
    }
}
<commit_msg>Write RGB images to nifti<commit_after>//! Utility functions to write nifti images.

use std::fs::File;
use std::io::BufWriter;
use std::ops::{Div, Sub};
use std::path::Path;

use byteorder::{LittleEndian, WriteBytesExt};
use flate2::write::GzEncoder;
use flate2::Compression;
use ndarray::{Array, ArrayBase, ArrayView, Axis, Data, Dimension, RemoveAxis, ScalarOperand};
use num_traits::FromPrimitive;
use safe_transmute::{guarded_transmute_to_bytes_pod_many, PodTransmutable};

use {util::is_gz_file, volume::element::DataElement, NiftiHeader, NiftiType, Result};

// TODO make this configurable. The Nifti standard does not specify a specific field for endianness,
// but it is encoded in `dim[0]`. "if dim[0] is outside range 1..7, then swap".
type B = LittleEndian;

/// Write a nifti file (.nii or .nii.gz) in Little Endian.
///
/// If a `reference` is given, it will be used to fill most of the header's fields. The voxels
/// intensity will be subtracted by `scl_slope` and divided by `scl_inter`. If `reference` is not
/// given, a default `NiftiHeader` will be built and written.
///
/// In all cases, the `dim`, `datatype` and `bitpix` fields will depend only on `data`, not on the
/// header. In other words, the `datatype` defined in `reference` will be ignored.
pub fn write_nifti<P, A, S, D>(
    path: P,
    data: &ArrayBase<S, D>,
    reference: Option<&NiftiHeader>,
) -> Result<()>
where
    P: AsRef<Path>,
    S: Data<Elem = A>,
    A: Copy,
    A: DataElement,
    A: Div<Output = A>,
    A: FromPrimitive,
    A: PodTransmutable,
    A: ScalarOperand,
    A: Sub<Output = A>,
    D: Dimension + RemoveAxis,
{
    let header = build_header(data, reference, A::DATA_TYPE);

    // Need the transpose for fortran ordering used in nifti file format.
    let data = data.t();

    let f = File::create(&path)?;
    let mut writer = BufWriter::new(f);
    if is_gz_file(&path) {
        let mut e = GzEncoder::new(writer, Compression::fast());
        write_header(&mut e, &header)?;
        write_data(&mut e, header, data)?;
        let _ = e.finish()?; // Must use result
    } else {
        write_header(&mut writer, &header)?;
        write_data(&mut writer, header, data)?;
    }
    Ok(())
}

/// Write a RGB nifti file (.nii or .nii.gz) in Little Endian.
///
/// If a `reference` is given, it will be used to fill most of the header's fields, except those
/// necessary to be recognized as a RGB image. `scl_slope` will be set to 1.0 and `scl_inter` to
/// 0.0.  If `reference` is not given, a default `NiftiHeader` will be built and written.
pub fn write_rgb_nifti<P, D>(
    path: P,
    data: &Array<[u8; 3], D>,
    reference: Option<&NiftiHeader>
) -> Result<()>
where
    P: AsRef<Path>,
    D: Dimension + RemoveAxis,
{
    // Copy most of the reference header, but the `bitpix`, `scl_slope` and `scl_inter` fields must
    // have those exact values.
    let mut header = build_header(data, reference, NiftiType::Rgb24);
    header.bitpix = 24;
    header.scl_slope = 1.0;
    header.scl_inter = 0.0;

    // Need the transpose for fortran used in nifti file format.
    let data = data.t();

    let f = File::create(&path)?;
    let mut writer = BufWriter::new(f);
    if is_gz_file(&path) {
        let mut e = GzEncoder::new(writer, Compression::fast());
        write_header(&mut e, &header)?;
        write_slices(&mut e, data)?;
        let _ = e.finish().unwrap(); // Must use result
    } else {
        write_header(&mut writer, &header)?;
        write_slices(&mut writer, data)?;
    }
    Ok(())
}

fn build_header<T, D>(
    data: &ArrayBase<T, D>,
    reference: Option<&NiftiHeader>,
    datatype: NiftiType
) -> NiftiHeader
where
    T: Data,
    D: Dimension
{
    let mut dim = [1; 8];
    dim[0] = data.ndim() as u16;
    for (i, s) in data.shape().iter().enumerate() {
        dim[i + 1] = *s as u16;
    }

    // If no reference header is given, use the default.
    let reference = match reference {
        Some(r) => r.clone(),
        None => NiftiHeader::default(),
    };
    NiftiHeader {
        dim,
        datatype: datatype as i16,
        bitpix: (datatype.size_of() * 8) as i16,
        // All other fields are copied from reference header
        ..reference
    }
}

fn write_header<W>(writer: &mut W, header: &NiftiHeader) -> Result<()>
where
    W: WriteBytesExt,
{
    writer.write_i32::<B>(header.sizeof_hdr)?;
    writer.write_all(&header.data_type)?;
    writer.write_all(&header.db_name)?;
    writer.write_i32::<B>(header.extents)?;
    writer.write_i16::<B>(header.session_error)?;
    writer.write_u8(header.regular)?;
    writer.write_u8(header.dim_info)?;
    for s in &header.dim {
        writer.write_u16::<B>(*s)?;
    }
    writer.write_f32::<B>(header.intent_p1)?;
    writer.write_f32::<B>(header.intent_p2)?;
    writer.write_f32::<B>(header.intent_p3)?;
    writer.write_i16::<B>(header.intent_code)?;
    writer.write_i16::<B>(header.datatype)?;
    writer.write_i16::<B>(header.bitpix)?;
    writer.write_i16::<B>(header.slice_start)?;
    for f in &header.pixdim {
        writer.write_f32::<B>(*f)?;
    }
    writer.write_f32::<B>(header.vox_offset)?;
    writer.write_f32::<B>(header.scl_slope)?;
    writer.write_f32::<B>(header.scl_inter)?;
    writer.write_i16::<B>(header.slice_end)?;
    writer.write_u8(header.slice_code)?;
    writer.write_u8(header.xyzt_units)?;
    writer.write_f32::<B>(header.cal_max)?;
    writer.write_f32::<B>(header.cal_min)?;
    writer.write_f32::<B>(header.slice_duration)?;
    writer.write_f32::<B>(header.toffset)?;
    writer.write_i32::<B>(header.glmax)?;
    writer.write_i32::<B>(header.glmin)?;

    writer.write_all(&header.descrip)?;
    writer.write_all(&header.aux_file)?;
    writer.write_i16::<B>(header.qform_code)?;
    writer.write_i16::<B>(header.sform_code)?;
    for f in &[
        header.quatern_b,
        header.quatern_c,
        header.quatern_d,
        header.quatern_x,
        header.quatern_y,
        header.quatern_z,
    ] {
        writer.write_f32::<B>(*f)?;
    }
    for f in header
        .srow_x
        .iter()
        .chain(&header.srow_y)
        .chain(&header.srow_z)
    {
        writer.write_f32::<B>(*f)?;
    }
    writer.write_all(&header.intent_name)?;
    writer.write_all(&header.magic)?;

    // Empty 4 bytes after the header
    // TODO Support writing extension data.
    writer.write_u32::<B>(0)?;

    Ok(())
}

/// Write the data in 'f' order.
///
/// Like NiBabel, we iterate by "slice" to improve speed and use less memory.
fn write_data<T, D, W>(writer: &mut W, header: NiftiHeader, data: ArrayView<T, D>) -> Result<()>
where
    T: Clone + PodTransmutable,
    T: Div<Output = T>,
    T: FromPrimitive,
    T: PodTransmutable,
    T: ScalarOperand,
    T: Sub<Output = T>,
    D: Dimension + RemoveAxis,
    W: WriteBytesExt,
{
    // `1.0x + 0.0` would give the same results, but we avoid a lot of divisions
    let slope = if header.scl_slope == 0.0 {
        1.0
    } else {
        header.scl_slope
    };
    if slope != 1.0 || header.scl_inter != 0.0 {
        let slope = T::from_f32(slope).unwrap();
        let inter = T::from_f32(header.scl_inter).unwrap();
        for arr_data in data.axis_iter(Axis(0)) {
            write_slice(writer, arr_data.sub(inter).div(slope))?;
        }
    } else {
        write_slices(writer, data)?;
    }
    Ok(())
}

fn write_slices<T, D, W>(writer: &mut W, data: ArrayView<T, D>) -> Result<()>
where
    T: Clone + PodTransmutable,
    D: Dimension + RemoveAxis,
    W: WriteBytesExt
{
    for arr_data in data.axis_iter(Axis(0)) {
        // We need to own the data because of the into_shape() for `C` ordering.
        write_slice(writer, arr_data.to_owned())?;
    }
    Ok(())
}

fn write_slice<T, D, W>(writer: &mut W, data: Array<T, D>) -> Result<()>
where
    T: Clone + PodTransmutable,
    D: Dimension,
    W: WriteBytesExt
{
    let len = data.len();
    let arr_data = data.into_shape(len).unwrap();
    let slice = arr_data.as_slice().unwrap();
    writer.write_all(guarded_transmute_to_bytes_pod_many(slice))?;
    Ok(())
}

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

    use super::*;
    use std::ops::{Add, Mul};
    use std::path::PathBuf;

    use self::tempfile::tempdir;
    use ndarray::{Array, Array2, Ix2, IxDyn, ShapeBuilder};

    use {header::MAGIC_CODE_NIP1, object::NiftiObject, InMemNiftiObject, IntoNdArray};

    fn get_temporary_path(ext: &str) -> PathBuf {
        let dir = tempdir().unwrap();
        let mut path = dir.into_path();
        if !ext.is_empty() {
            path.push(ext);
        }
        path
    }

    pub fn generate_nifti_header(
        dim: [u16; 8],
        scl_slope: f32,
        scl_inter: f32,
        datatype: i16,
    ) -> NiftiHeader {
        NiftiHeader {
            dim,
            datatype,
            bitpix: (NiftiType::Float32.size_of() * 8) as i16,
            magic: *MAGIC_CODE_NIP1,
            scl_slope,
            scl_inter,
            ..NiftiHeader::default()
        }
    }

    fn read_2d_image<P>(path: P) -> Array2<f32>
    where
        P: AsRef<Path>,
    {
        let nifti_object = InMemNiftiObject::from_file(path).expect("Nifti file is unreadable.");
        let volume = nifti_object.into_volume();
        let dyn_data = volume.into_ndarray().unwrap();
        dyn_data.into_dimensionality::<Ix2>().unwrap()
    }

    fn test_write_read(arr: &Array<f32, IxDyn>, path: &str) {
        let path = get_temporary_path(path);
        let mut dim = [1; 8];
        dim[0] = arr.ndim() as u16;
        for (i, s) in arr.shape().iter().enumerate() {
            dim[i + 1] = *s as u16;
        }
        let header = generate_nifti_header(dim, 1.0, 0.0, 16);
        write_nifti(&path, &arr, Some(&header)).unwrap();

        let read_nifti = read_2d_image(path);
        assert!(read_nifti.all_close(&arr, 1e-10));
    }

    fn f_order_array() -> Array<f32, IxDyn> {
        let dim = vec![4, 4];
        let vec = (0..16).map(|x| x as f32).collect();
        Array::from_shape_vec(IxDyn(&dim).f(), vec).unwrap()
    }

    fn c_order_array() -> Array<f32, IxDyn> {
        let dim = vec![4, 4];
        let vec = (0..16).map(|x| x as f32).collect();
        Array::from_shape_vec(IxDyn(&dim), vec).unwrap()
    }

    #[test]
    fn test_fortran_writing() {
        // Test .nii
        let arr = f_order_array();
        test_write_read(&arr, "test.nii");
        let mut arr = f_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii");

        // Test .nii.gz
        let arr = f_order_array();
        test_write_read(&arr, "test.nii.gz");
        let mut arr = f_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii.gz");
    }

    #[test]
    fn test_c_writing() {
        // Test .nii
        let arr = c_order_array();
        test_write_read(&arr, "test.nii");

        let mut arr = c_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii");

        // Test .nii.gz
        let arr = c_order_array();
        test_write_read(&arr, "test.nii.gz");

        let mut arr = c_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii.gz");
    }

    #[test]
    fn test_header_slope_inter() {
        let arr = f_order_array();
        let slope = 2.2;
        let inter = 101.1;

        let path = get_temporary_path("test_slope_inter.nii");
        let mut dim = [1; 8];
        dim[0] = arr.ndim() as u16;
        for (i, s) in arr.shape().iter().enumerate() {
            dim[i + 1] = *s as u16;
        }
        let header = generate_nifti_header(dim, slope, inter, 16);
        let transformed_data = arr.mul(slope).add(inter);
        write_nifti(&path, &transformed_data, Some(&header)).unwrap();

        let read_nifti = read_2d_image(path);
        assert!(read_nifti.all_close(&transformed_data, 1e-10));
    }
}
<|endoftext|>


<commit_before>use std::cmp;
use std::collections::VecDeque;
use AttackResult::*;
use EffectClass::*;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Character {
    hp: i32,
    mana: i32,
    damage: i32,
    armor: i32
}

#[derive(Debug, Clone)]
enum AttackResult {
    Win,
    Lose,
    Continue(State)
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum EffectClass {
    Shield,
    Poison,
    Recharge
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Effect {
    turns: i32,
    intensity: i32,
    class: EffectClass
}

#[derive(Debug, Copy, Clone)]
struct Spell {
    cost: i32,
    damage: i32,
    healing: i32,
    effect: Option<Effect>
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct State {
    player: Character,
    boss: Character,
    effects: Vec<Effect>
}

fn process_effects(state: &mut State) {
    let mut effects = Vec::new();
    let mut armor = 0;

    for mut effect in state.effects.iter().filter(|x| x.turns > 0).cloned() {
        effect.turns -= 1;

        if effect.turns > 0 {
            effects.push(effect);
        }

        match effect.class {
            Shield => armor = effect.intensity,
            Poison => state.boss.hp -= effect.intensity,
            Recharge => state.player.mana += effect.intensity
        }
    }

    state.player.armor = armor;
    state.effects = effects;
}

fn cast_spell(state: &State, spell: Spell) -> AttackResult {
    let mut result = state.clone();
    process_effects(&mut result);

    // Cast spell

    result.player.mana -= spell.cost;
    result.player.hp += spell.healing;
    result.boss.hp -= spell.damage;

    if result.player.mana <= 0 {
        return Lose;
    }

    // Deal with effects

    if let Some(effect) = spell.effect {
        if result.effects.iter().all(|x| x.class != effect.class) {
            result.effects.push(effect);
        } else {
            return Lose;
        }
    }

    // Counter attack

    process_effects(&mut result);

    if result.boss.hp > 0 {
        let damage = cmp::max(result.boss.damage - result.player.armor, 1);
        result.player.hp = cmp::max(result.player.hp - damage, 0);

        if result.player.hp <= 0 {
            return Lose;
        }
    } else {
        return Win;
    }

    Continue(result)
}

fn cheapest_strategy(spells: &Vec<Spell>, start: &State) -> Option<Vec<Spell>> {
    let mut queue: VecDeque<(Vec<Spell>, State)> = VecDeque::new();
    let mut min = None;

    queue.push_back((vec![], start.clone()));

    while queue.len() > 0 {
        let (substrategy, state) = queue.pop_front().unwrap();
        let cost = substrategy.iter().map(|spell| spell.cost).sum::<i32>();

        for &spell in spells {
            match cast_spell(&state, spell) {
                Lose => continue,
                attack_result => {
                    if let Some((min_cost, _)) = min {
                        if cost + spell.cost >= min_cost {
                            continue;
                        }
                    }

                    let mut strategy = substrategy.clone();
                    strategy.push(spell);

                    if let Continue(next_state) = attack_result {
                        queue.push_back((strategy, next_state));
                    } else {
                        min = Some((cost + spell.cost, strategy));
                    }
                }
            }
        }
    }

    match min {
        None => None,
        Some((_, mut strategy)) => {
            strategy.reverse();
            Some(strategy)
        }
    }
}

fn main() {
    let spells = vec![
        Spell {
            cost: 53,
            damage: 4,
            healing: 0,
            effect: None
        },
        Spell {
            cost: 73,
            damage: 2,
            healing: 2,
            effect: None
        },
        Spell {
            cost: 113,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 6, intensity: 7, class: Shield})
        },
        Spell {
            cost: 173,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 6, intensity: 3, class: Poison})
        },
        Spell {
            cost: 229,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 5, intensity: 101, class: Recharge})
        }
    ];

    let boss = Character {
        hp: 51,
        mana: 0,
        damage: 9,
        armor: 0
    };

    let player = Character {
        hp: 50,
        mana: 500,
        damage: 0,
        armor: 0
    };

    let start = State {player, boss, effects: Vec::new()};
    let strategy = cheapest_strategy(&spells, &start).unwrap();
    let cost = strategy.iter().map(|spell| spell.cost).sum::<i32>();

    println!("Part 1: {}", cost);
}
<commit_msg>Add 2015 day 22 part 2 solution<commit_after>use std::cmp;
use std::collections::VecDeque;
use AttackResult::*;
use EffectClass::*;

#[derive(Debug, Copy, Clone, PartialEq)]
struct Character {
    hp: i32,
    mana: i32,
    damage: i32,
    armor: i32
}

#[derive(Debug, Clone)]
enum AttackResult {
    Win,
    Lose,
    Continue(State)
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum EffectClass {
    Shield,
    Poison,
    Recharge
}

#[derive(Debug, Copy, Clone, PartialEq)]
struct Effect {
    turns: i32,
    intensity: i32,
    class: EffectClass
}

#[derive(Debug, Copy, Clone)]
struct Spell {
    cost: i32,
    damage: i32,
    healing: i32,
    effect: Option<Effect>
}

#[derive(Debug, Clone, PartialEq)]
struct State {
    player: Character,
    boss: Character,
    effects: Vec<Effect>
}

fn process_effects(state: &mut State) {
    let mut effects = Vec::new();
    let mut armor = 0;

    for mut effect in state.effects.iter().filter(|x| x.turns > 0).cloned() {
        effect.turns -= 1;

        if effect.turns > 0 {
            effects.push(effect);
        }

        match effect.class {
            Shield => armor = effect.intensity,
            Poison => state.boss.hp -= effect.intensity,
            Recharge => state.player.mana += effect.intensity
        }
    }

    state.player.armor = armor;
    state.effects = effects;
}

fn cast_spell(state: &State, spell: Spell, hard: bool) -> AttackResult {
    let mut result = state.clone();

    if hard {
        result.player.hp -= 1;

        if result.player.hp <= 0 {
            return Lose;
        }
    }

    process_effects(&mut result);

    // Cast spell

    result.player.mana -= spell.cost;
    result.player.hp += spell.healing;
    result.boss.hp -= spell.damage;

    if result.player.mana <= 0 {
        return Lose;
    }

    // Deal with effects

    if let Some(effect) = spell.effect {
        if result.effects.iter().all(|x| x.class != effect.class) {
            result.effects.push(effect);
        } else {
            return Lose;
        }
    }

    // Counter attack

    process_effects(&mut result);

    if result.boss.hp > 0 {
        let damage = cmp::max(result.boss.damage - result.player.armor, 1);
        result.player.hp = cmp::max(result.player.hp - damage, 0);

        if result.player.hp <= 0 {
            return Lose;
        }
    } else {
        return Win;
    }

    Continue(result)
}

fn cheapest_strategy(spells: &Vec<Spell>, start: &State, hard: bool) -> Option<Vec<Spell>> {
    let mut queue: VecDeque<(Vec<Spell>, State)> = VecDeque::new();
    let mut min = None;

    queue.push_back((vec![], start.clone()));

    while queue.len() > 0 {
        let (substrategy, state) = queue.pop_front().unwrap();
        let cost = substrategy.iter().map(|spell| spell.cost).sum::<i32>();

        for &spell in spells {
            let attack_result = cast_spell(&state, spell, hard);

            if let Lose = attack_result {
                continue;
            }

            if let Some((min_cost, _)) = min {
                if cost + spell.cost >= min_cost {
                    continue;
                }
            }

            let mut strategy = substrategy.clone();
            strategy.push(spell);

            if let Continue(next_state) = attack_result {
                queue.push_back((strategy, next_state));
            } else {
                min = Some((cost + spell.cost, strategy));
            }
        }
    }

    match min {
        None => None,
        Some((_, mut strategy)) => {
            strategy.reverse();
            Some(strategy)
        }
    }
}

fn main() {
    let spells = vec![
        Spell {
            cost: 53,
            damage: 4,
            healing: 0,
            effect: None
        },
        Spell {
            cost: 73,
            damage: 2,
            healing: 2,
            effect: None
        },
        Spell {
            cost: 113,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 6, intensity: 7, class: Shield})
        },
        Spell {
            cost: 173,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 6, intensity: 3, class: Poison})
        },
        Spell {
            cost: 229,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 5, intensity: 101, class: Recharge})
        }
    ];

    let boss = Character {
        hp: 51,
        mana: 0,
        damage: 9,
        armor: 0
    };

    let player = Character {
        hp: 50,
        mana: 500,
        damage: 0,
        armor: 0
    };

    let start = State {player, boss, effects: Vec::new()};
    let cost = |strategy: &Vec<Spell>| strategy.iter().map(|spell| spell.cost).sum::<i32>();

    let strategy = cheapest_strategy(&spells, &start, false).unwrap();
    println!("Part 1: {}", cost(&strategy));

    let strategy = cheapest_strategy(&spells, &start, true).unwrap();
    println!("Part 2: {}", cost(&strategy));
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("24.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

#[derive(Debug, Clone)]
struct GroupInfo {
    id: usize,
    friendly: bool,
    units: usize,
    hp: usize,
    weaknesses: Vec<String>,
    immunities: Vec<String>,
    attack_type: String,
    damage: usize,
    initiative: usize
}

fn parse(input: &str) -> Vec<GroupInfo> {
    let input = input.trim().replace('\r', "");

    input.split("\n\n")
    .enumerate()
    .flat_map(|(i, part)| {
        part.trim().lines()
        .filter(|line| !line.contains(':'))
        .map(|line| line.split(|c| ['(', ')'].contains(&c)).collect::<Vec<_>>())
        .filter_map(|line_parts| match line_parts.len() {
            1 => Some((line_parts[0].trim().split(' ').chain("".split(' ')), None)),
            3 => Some((
                line_parts[0].trim().split(' ').chain(line_parts[2].trim().split(' ')),
                Some(line_parts[1])
            )),
            _ => None
        })
        .enumerate()
        .filter_map(move |(j, (mut part1, part2))| {
            let friendly = i == 0;
            let units = part1.nth(0)
                .and_then(|x| x.parse::<usize>().ok())
                .unwrap_or(0);
            let hp = part1.nth(3)
                .and_then(|x| x.parse::<usize>().ok())
                .unwrap_or(0);
            let damage = part1.nth(7)
                .and_then(|x| x.parse::<usize>().ok())
                .unwrap_or(0);
            let attack_type = part1.nth(0)
                .map(|x| x.to_string())
                .unwrap_or(String::new());
            let initiative = part1.nth(3)
                .and_then(|x| x.parse::<usize>().ok())
                .unwrap_or(0);

            let (weaknesses, immunities) = part2
                .map(|s| {
                    let mut hash_map = s.split("; ")
                        .filter_map(|part| {
                            let mut tokens = part.split(", ").collect::<Vec<_>>();
                            let key = tokens.get(0).and_then(|x| x.split(' ').next());

                            tokens.get_mut(0)
                            .and_then(|x| x.split(' ').nth(2).map(|v| *x = v));

                            key.map(|key| (key, tokens))
                        })
                        .fold(HashMap::new(), |mut acc, (k, v)| {
                            acc.insert(k, v);
                            acc
                        });

                    (
                        hash_map.remove(&"weak").unwrap_or_else(|| vec![]),
                        hash_map.remove(&"immune").unwrap_or_else(|| vec![])
                    )
                })
                .map(|(v, w)| (
                    v.into_iter().map(|x| x.to_string()).collect(),
                    w.into_iter().map(|x| x.to_string()).collect()
                ))
                .unwrap_or_else(|| (vec![], vec![]));

            Some(GroupInfo {
                id: i * 100 + j,
                friendly,
                units,
                hp,
                weaknesses,
                immunities,
                attack_type,
                damage,
                initiative
            })
        })
    })
    .collect()
}

fn select_target<'a>(group: &GroupInfo, targets: &[&GroupInfo]) -> Option<usize> {
    targets.iter()
    .map(|x| *x)
    .filter(|target| target.friendly != group.friendly)
    .filter(|target| get_actual_damage(group, target) > 0)
    .max_by_key(|target| (
        get_actual_damage(group, target),
        get_effective_power(target),
        target.initiative
    ))
    .map(|target| target.id)
}

fn get_attack_order(groups: &mut [GroupInfo]) -> Vec<(usize, Option<usize>)> {
    let mut result = Vec::new();
    let mut selected = Vec::new();

    groups.sort_by_key(|g| (get_effective_power(g), g.initiative));
    groups.reverse();

    for group in groups.iter() {
        let targets = groups.iter()
            .filter(|target| !selected.contains(&target.id))
            .collect::<Vec<_>>();

        let target = select_target(group, &targets);

        target.map(|t| selected.push(t));
        result.push((group.initiative, group.id, target));
    }

    result.sort();
    result.reverse();

    result.into_iter()
    .map(|(_, id, target)| (id, target))
    .collect()
}

fn get_effective_power(group: &GroupInfo) -> usize {
    group.units * group.damage
}

fn get_actual_damage(group: &GroupInfo, target: &GroupInfo) -> usize {
    let mut damage = get_effective_power(group);

    if target.immunities.contains(&group.attack_type) {
        damage = 0;
    } else if target.weaknesses.contains(&group.attack_type) {
        damage *= 2;
    }

    damage
}

fn attack(group: &GroupInfo, target: &GroupInfo) -> Option<GroupInfo> {
    let actual_damage = get_actual_damage(group, target);
    let lost_units = actual_damage / target.hp;

    if target.units > lost_units {
        let mut after = target.clone();
        after.units = target.units - lost_units;
        Some(after)
    } else {
        None
    }
}

fn fight(mut groups: Vec<GroupInfo>) -> Option<Vec<GroupInfo>> {
    while groups.len() > 0 && !groups.iter().all(|g| g.friendly == groups[0].friendly) {
        let attack_order = get_attack_order(&mut groups);
        let mut change = false;

        for (attacker_id, defender_id) in attack_order.into_iter() {
            if defender_id.is_none() {
                continue;
            }

            let defender_id = defender_id.unwrap();
            let mut defender_units = 0;

            let defender_after = {
                let attacker = groups.iter().find(|g| g.id == attacker_id);
                let defender = groups.iter().find(|g| g.id == defender_id);

                match (attacker, defender) {
                    (Some(x), Some(y)) => {
                        defender_units = y.units;
                        Some(attack(x, y))
                    },
                    _ => None
                }
            };

            groups = match defender_after {
                Some(Some(defender_after)) => {
                    // Defender lives

                    if defender_after.units != defender_units {
                        change = true;
                    }

                    groups.into_iter()
                    .map(|g| if g.id == defender_id { defender_after.clone() } else { g })
                    .collect()
                },
                Some(None) => {
                    // Defender dies

                    change = true;

                    groups.into_iter()
                    .filter(|g| g.id != defender_id)
                    .collect()
                },
                None => {
                    // Attacker or defender died already

                    groups
                }
            };
        }

        if !change {
            return None;
        }
    }

    Some(groups)
}

fn main() {
    let input = get_input().unwrap();
    let groups = parse(&input);

    fight(groups.clone())
    .map(|final_groups| {
        final_groups.iter()
        .map(|g| g.units)
        .sum::<usize>()
    })
    .map(|unit_count| println!("Part 1: {}", unit_count));

    (1..)
    .inspect(|x| if x % 1000 == 0 { println!("{}", x) })
    .filter_map(|boost| {
        let boosted_groups = groups.iter()
            .cloned()
            .map(|mut group| {
                if group.friendly {
                    group.damage += boost;
                }

                group
            })
            .collect::<Vec<_>>();

        fight(boosted_groups)
    })
    .find(|final_groups| final_groups.len() > 0 && final_groups[0].friendly)
    .map(|final_groups| {
        final_groups.iter()
        .map(|g| g.units)
        .sum::<usize>()
    })
    .map(|unit_count| println!("Part 2: {}", unit_count));
}
<commit_msg>Remove debug printing<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("24.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

#[derive(Debug, Clone)]
struct GroupInfo {
    id: usize,
    friendly: bool,
    units: usize,
    hp: usize,
    weaknesses: Vec<String>,
    immunities: Vec<String>,
    attack_type: String,
    damage: usize,
    initiative: usize
}

fn parse(input: &str) -> Vec<GroupInfo> {
    let input = input.trim().replace('\r', "");

    input.split("\n\n")
    .enumerate()
    .flat_map(|(i, part)| {
        part.trim().lines()
        .filter(|line| !line.contains(':'))
        .map(|line| line.split(|c| ['(', ')'].contains(&c)).collect::<Vec<_>>())
        .filter_map(|line_parts| match line_parts.len() {
            1 => Some((line_parts[0].trim().split(' ').chain("".split(' ')), None)),
            3 => Some((
                line_parts[0].trim().split(' ').chain(line_parts[2].trim().split(' ')),
                Some(line_parts[1])
            )),
            _ => None
        })
        .enumerate()
        .filter_map(move |(j, (mut part1, part2))| {
            let friendly = i == 0;
            let units = part1.nth(0)
                .and_then(|x| x.parse::<usize>().ok())
                .unwrap_or(0);
            let hp = part1.nth(3)
                .and_then(|x| x.parse::<usize>().ok())
                .unwrap_or(0);
            let damage = part1.nth(7)
                .and_then(|x| x.parse::<usize>().ok())
                .unwrap_or(0);
            let attack_type = part1.nth(0)
                .map(|x| x.to_string())
                .unwrap_or(String::new());
            let initiative = part1.nth(3)
                .and_then(|x| x.parse::<usize>().ok())
                .unwrap_or(0);

            let (weaknesses, immunities) = part2
                .map(|s| {
                    let mut hash_map = s.split("; ")
                        .filter_map(|part| {
                            let mut tokens = part.split(", ").collect::<Vec<_>>();
                            let key = tokens.get(0).and_then(|x| x.split(' ').next());

                            tokens.get_mut(0)
                            .and_then(|x| x.split(' ').nth(2).map(|v| *x = v));

                            key.map(|key| (key, tokens))
                        })
                        .fold(HashMap::new(), |mut acc, (k, v)| {
                            acc.insert(k, v);
                            acc
                        });

                    (
                        hash_map.remove(&"weak").unwrap_or_else(|| vec![]),
                        hash_map.remove(&"immune").unwrap_or_else(|| vec![])
                    )
                })
                .map(|(v, w)| (
                    v.into_iter().map(|x| x.to_string()).collect(),
                    w.into_iter().map(|x| x.to_string()).collect()
                ))
                .unwrap_or_else(|| (vec![], vec![]));

            Some(GroupInfo {
                id: i * 100 + j,
                friendly,
                units,
                hp,
                weaknesses,
                immunities,
                attack_type,
                damage,
                initiative
            })
        })
    })
    .collect()
}

fn select_target<'a>(group: &GroupInfo, targets: &[&GroupInfo]) -> Option<usize> {
    targets.iter()
    .map(|x| *x)
    .filter(|target| target.friendly != group.friendly)
    .filter(|target| get_actual_damage(group, target) > 0)
    .max_by_key(|target| (
        get_actual_damage(group, target),
        get_effective_power(target),
        target.initiative
    ))
    .map(|target| target.id)
}

fn get_attack_order(groups: &mut [GroupInfo]) -> Vec<(usize, Option<usize>)> {
    let mut result = Vec::new();
    let mut selected = Vec::new();

    groups.sort_by_key(|g| (get_effective_power(g), g.initiative));
    groups.reverse();

    for group in groups.iter() {
        let targets = groups.iter()
            .filter(|target| !selected.contains(&target.id))
            .collect::<Vec<_>>();

        let target = select_target(group, &targets);

        target.map(|t| selected.push(t));
        result.push((group.initiative, group.id, target));
    }

    result.sort();
    result.reverse();

    result.into_iter()
    .map(|(_, id, target)| (id, target))
    .collect()
}

fn get_effective_power(group: &GroupInfo) -> usize {
    group.units * group.damage
}

fn get_actual_damage(group: &GroupInfo, target: &GroupInfo) -> usize {
    let mut damage = get_effective_power(group);

    if target.immunities.contains(&group.attack_type) {
        damage = 0;
    } else if target.weaknesses.contains(&group.attack_type) {
        damage *= 2;
    }

    damage
}

fn attack(group: &GroupInfo, target: &GroupInfo) -> Option<GroupInfo> {
    let actual_damage = get_actual_damage(group, target);
    let lost_units = actual_damage / target.hp;

    if target.units > lost_units {
        let mut after = target.clone();
        after.units = target.units - lost_units;
        Some(after)
    } else {
        None
    }
}

fn fight(mut groups: Vec<GroupInfo>) -> Option<Vec<GroupInfo>> {
    while groups.len() > 0 && !groups.iter().all(|g| g.friendly == groups[0].friendly) {
        let attack_order = get_attack_order(&mut groups);
        let mut change = false;

        for (attacker_id, defender_id) in attack_order.into_iter() {
            if defender_id.is_none() {
                continue;
            }

            let defender_id = defender_id.unwrap();
            let mut defender_units = 0;

            let defender_after = {
                let attacker = groups.iter().find(|g| g.id == attacker_id);
                let defender = groups.iter().find(|g| g.id == defender_id);

                match (attacker, defender) {
                    (Some(x), Some(y)) => {
                        defender_units = y.units;
                        Some(attack(x, y))
                    },
                    _ => None
                }
            };

            groups = match defender_after {
                Some(Some(defender_after)) => {
                    // Defender lives

                    if defender_after.units != defender_units {
                        change = true;
                    }

                    groups.into_iter()
                    .map(|g| if g.id == defender_id { defender_after.clone() } else { g })
                    .collect()
                },
                Some(None) => {
                    // Defender dies

                    change = true;

                    groups.into_iter()
                    .filter(|g| g.id != defender_id)
                    .collect()
                },
                None => {
                    // Attacker or defender died already

                    groups
                }
            };
        }

        if !change {
            return None;
        }
    }

    Some(groups)
}

fn main() {
    let input = get_input().unwrap();
    let groups = parse(&input);

    fight(groups.clone())
    .map(|final_groups| {
        final_groups.iter()
        .map(|g| g.units)
        .sum::<usize>()
    })
    .map(|unit_count| println!("Part 1: {}", unit_count));

    (1..)
    .filter_map(|boost| {
        let boosted_groups = groups.iter()
            .cloned()
            .map(|mut group| {
                if group.friendly {
                    group.damage += boost;
                }

                group
            })
            .collect::<Vec<_>>();

        fight(boosted_groups)
    })
    .find(|final_groups| final_groups.len() > 0 && final_groups[0].friendly)
    .map(|final_groups| {
        final_groups.iter()
        .map(|g| g.units)
        .sum::<usize>()
    })
    .map(|unit_count| println!("Part 2: {}", unit_count));
}
<|endoftext|>


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

use dom::bindings::error::Fallible;
use dom::bindings::error::Error::DataClone;
use dom::bindings::global::GlobalRef;

use js::glue::JS_STRUCTURED_CLONE_VERSION;
use js::jsapi::JSContext;
use js::jsapi::{JS_WriteStructuredClone, JS_ClearPendingException};
use js::jsapi::JS_ReadStructuredClone;
use js::jsval::{JSVal, UndefinedValue};

use libc::size_t;
use std::ptr;

#[allow(raw_pointer_deriving)]
#[deriving(Copy)]
pub struct StructuredCloneData {
    pub data: *mut u64,
    pub nbytes: size_t,
}

impl StructuredCloneData {
    pub fn write(cx: *mut JSContext, message: JSVal)
                 -> Fallible<StructuredCloneData> {
        let mut data = ptr::null_mut();
        let mut nbytes = 0;
        let result = unsafe {
            JS_WriteStructuredClone(cx, message, &mut data, &mut nbytes,
                                    ptr::null(), ptr::null_mut())
        };
        if result == 0 {
            unsafe { JS_ClearPendingException(cx); }
            return Err(DataClone);
        }
        Ok(StructuredCloneData {
            data: data,
            nbytes: nbytes,
        })
    }

    pub fn read(self, global: GlobalRef) -> JSVal {
        let mut message = UndefinedValue();
        unsafe {
            assert!(JS_ReadStructuredClone(
                global.get_cx(), self.data as *const u64, self.nbytes,
                JS_STRUCTURED_CLONE_VERSION, &mut message,
                ptr::null(), ptr::null_mut()) != 0);
        }
        message
    }
}
<commit_msg>Make StructuredCloneData an opaque struct.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::error::Fallible;
use dom::bindings::error::Error::DataClone;
use dom::bindings::global::GlobalRef;

use js::glue::JS_STRUCTURED_CLONE_VERSION;
use js::jsapi::JSContext;
use js::jsapi::{JS_WriteStructuredClone, JS_ClearPendingException};
use js::jsapi::JS_ReadStructuredClone;
use js::jsval::{JSVal, UndefinedValue};

use libc::size_t;
use std::ptr;

pub struct StructuredCloneData {
    data: *mut u64,
    nbytes: size_t,
}

impl StructuredCloneData {
    pub fn write(cx: *mut JSContext, message: JSVal)
                 -> Fallible<StructuredCloneData> {
        let mut data = ptr::null_mut();
        let mut nbytes = 0;
        let result = unsafe {
            JS_WriteStructuredClone(cx, message, &mut data, &mut nbytes,
                                    ptr::null(), ptr::null_mut())
        };
        if result == 0 {
            unsafe { JS_ClearPendingException(cx); }
            return Err(DataClone);
        }
        Ok(StructuredCloneData {
            data: data,
            nbytes: nbytes,
        })
    }

    pub fn read(self, global: GlobalRef) -> JSVal {
        let mut message = UndefinedValue();
        unsafe {
            assert!(JS_ReadStructuredClone(
                global.get_cx(), self.data as *const u64, self.nbytes,
                JS_STRUCTURED_CLONE_VERSION, &mut message,
                ptr::null(), ptr::null_mut()) != 0);
        }
        message
    }
}
<|endoftext|>


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

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("List", inherited=True) %>

${helpers.single_keyword("list-style-position", "outside inside", animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-lists/#propdef-list-style-position")}

// TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1:
//
//     decimal-leading-zero, armenian, upper-armenian, lower-armenian, georgian, lower-roman,
//     upper-roman
//
// TODO(bholley): Missing quite a few gecko properties here as well.
//
// In gecko, {upper,lower}-{roman,alpha} are implemented as @counter-styles in the
// UA, however they can also be set from pres attrs. When @counter-style is supported
// we may need to look into this and handle these differently.
//
// [1]: http://dev.w3.org/csswg/css-counter-styles/
% if product == "servo":
    ${helpers.single_keyword("list-style-type", """
        disc none circle square decimal disclosure-open disclosure-closed lower-alpha upper-alpha
        arabic-indic bengali cambodian cjk-decimal devanagari gujarati gurmukhi kannada khmer lao
        malayalam mongolian myanmar oriya persian telugu thai tibetan cjk-earthly-branch
        cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana katakana-iroha""",
        animation_value_type="discrete",
        spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type")}
% else:
    <%helpers:longhand name="list-style-type" animation_value_type="discrete" boxed="True"
                       spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type">
        use values::CustomIdent;
        use values::generics::CounterStyleOrNone;

        pub use self::computed_value::T as SpecifiedValue;

        pub mod computed_value {
            use values::generics::CounterStyleOrNone;

            /// <counter-style> | <string> | none
            #[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss)]
            pub enum T {
                CounterStyle(CounterStyleOrNone),
                String(String),
            }
        }

        #[cfg(feature = "gecko")]
        impl SpecifiedValue {
            /// Convert from gecko keyword to list-style-type.
            ///
            /// This should only be used for mapping type attribute to
            /// list-style-type, and thus only values possible in that
            /// attribute is considered here.
            pub fn from_gecko_keyword(value: u32) -> Self {
                use gecko_bindings::structs;
                SpecifiedValue::CounterStyle(if value == structs::NS_STYLE_LIST_STYLE_NONE {
                    CounterStyleOrNone::None
                } else {
                    <%
                        values = """disc circle square decimal lower-roman
                                    upper-roman lower-alpha upper-alpha""".split()
                    %>
                    CounterStyleOrNone::Name(CustomIdent(match value {
                        % for style in values:
                        structs::NS_STYLE_LIST_STYLE_${style.replace('-', '_').upper()} => atom!("${style}"),
                        % endfor
                        _ => unreachable!("Unknown counter style keyword value"),
                    }))
                })
            }
        }

        #[inline]
        pub fn get_initial_value() -> computed_value::T {
            computed_value::T::CounterStyle(CounterStyleOrNone::disc())
        }

        #[inline]
        pub fn get_initial_specified_value() -> SpecifiedValue {
            SpecifiedValue::CounterStyle(CounterStyleOrNone::disc())
        }

        pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                             -> Result<SpecifiedValue, ParseError<'i>> {
            Ok(if let Ok(style) = input.try(|i| CounterStyleOrNone::parse(context, i)) {
                SpecifiedValue::CounterStyle(style)
            } else {
                SpecifiedValue::String(input.expect_string()?.as_ref().to_owned())
            })
        }
    </%helpers:longhand>
% endif

<%helpers:longhand name="list-style-image" animation_value_type="discrete"
                   boxed="${product == 'gecko'}"
                   spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image">
    use values::computed::ComputedValueAsSpecified;
    use values::specified::UrlOrNone;
    pub use self::computed_value::T as SpecifiedValue;

    pub mod computed_value {
        use values::specified::UrlOrNone;

        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        #[derive(Clone, Debug, PartialEq, ToCss)]
        pub struct T(pub UrlOrNone);
    }

    impl ComputedValueAsSpecified for SpecifiedValue {}

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T(Either::Second(None_))
    }
    #[inline]
    pub fn get_initial_specified_value() -> SpecifiedValue {
        SpecifiedValue(Either::Second(None_))
    }
    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue,ParseError<'i>> {
        % if product == "gecko":
        let mut value = input.try(|input| UrlOrNone::parse(context, input))?;
        if let Either::First(ref mut url) = value {
            url.build_image_value();
        }
        % else :
        let value = input.try(|input| UrlOrNone::parse(context, input))?;
        % endif

        return Ok(SpecifiedValue(value));
    }
</%helpers:longhand>

<%helpers:longhand name="quotes" animation_value_type="discrete"
                   spec="https://drafts.csswg.org/css-content/#propdef-quotes">
    use cssparser::serialize_string;
    use std::fmt;
    use style_traits::ToCss;
    use values::computed::ComputedValueAsSpecified;

    pub use self::computed_value::T as SpecifiedValue;

    pub mod computed_value {
        #[derive(Clone, Debug, PartialEq)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub struct T(pub Vec<(String,String)>);
    }

    impl ComputedValueAsSpecified for SpecifiedValue {}

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.0.is_empty() {
                return dest.write_str("none")
            }

            let mut first = true;
            for pair in &self.0 {
                if !first {
                    dest.write_str(" ")?;
                }
                first = false;
                serialize_string(&*pair.0, dest)?;
                dest.write_str(" ")?;
                serialize_string(&*pair.1, dest)?;
            }
            Ok(())
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T(vec![
            ("\u{201c}".to_owned(), "\u{201d}".to_owned()),
            ("\u{2018}".to_owned(), "\u{2019}".to_owned()),
        ])
    }

    pub fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue,ParseError<'i>> {
        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(SpecifiedValue(Vec::new()))
        }

        let mut quotes = Vec::new();
        loop {
            let first = match input.next() {
                Ok(&Token::QuotedString(ref value)) => value.as_ref().to_owned(),
                Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
                Err(_) => break,
            };
            let second = match input.next() {
                Ok(&Token::QuotedString(ref value)) => value.as_ref().to_owned(),
                Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
                Err(e) => return Err(e.into()),
            };
            quotes.push((first, second))
        }
        if !quotes.is_empty() {
            Ok(SpecifiedValue(quotes))
        } else {
            Err(StyleParseError::UnspecifiedError.into())
        }
    }
</%helpers:longhand>

${helpers.predefined_type("-moz-image-region",
                          "ClipRectOrAuto",
                          "computed::ClipRectOrAuto::auto()",
                          animation_value_type="ComputedValue",
                          products="gecko",
                          boxed="True",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-image-region)")}
<commit_msg>Do not use CVAS for the quotes property<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("List", inherited=True) %>

${helpers.single_keyword("list-style-position", "outside inside", animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-lists/#propdef-list-style-position")}

// TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1:
//
//     decimal-leading-zero, armenian, upper-armenian, lower-armenian, georgian, lower-roman,
//     upper-roman
//
// TODO(bholley): Missing quite a few gecko properties here as well.
//
// In gecko, {upper,lower}-{roman,alpha} are implemented as @counter-styles in the
// UA, however they can also be set from pres attrs. When @counter-style is supported
// we may need to look into this and handle these differently.
//
// [1]: http://dev.w3.org/csswg/css-counter-styles/
% if product == "servo":
    ${helpers.single_keyword("list-style-type", """
        disc none circle square decimal disclosure-open disclosure-closed lower-alpha upper-alpha
        arabic-indic bengali cambodian cjk-decimal devanagari gujarati gurmukhi kannada khmer lao
        malayalam mongolian myanmar oriya persian telugu thai tibetan cjk-earthly-branch
        cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana katakana-iroha""",
        animation_value_type="discrete",
        spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type")}
% else:
    <%helpers:longhand name="list-style-type" animation_value_type="discrete" boxed="True"
                       spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type">
        use values::CustomIdent;
        use values::generics::CounterStyleOrNone;

        pub use self::computed_value::T as SpecifiedValue;

        pub mod computed_value {
            use values::generics::CounterStyleOrNone;

            /// <counter-style> | <string> | none
            #[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss)]
            pub enum T {
                CounterStyle(CounterStyleOrNone),
                String(String),
            }
        }

        #[cfg(feature = "gecko")]
        impl SpecifiedValue {
            /// Convert from gecko keyword to list-style-type.
            ///
            /// This should only be used for mapping type attribute to
            /// list-style-type, and thus only values possible in that
            /// attribute is considered here.
            pub fn from_gecko_keyword(value: u32) -> Self {
                use gecko_bindings::structs;
                SpecifiedValue::CounterStyle(if value == structs::NS_STYLE_LIST_STYLE_NONE {
                    CounterStyleOrNone::None
                } else {
                    <%
                        values = """disc circle square decimal lower-roman
                                    upper-roman lower-alpha upper-alpha""".split()
                    %>
                    CounterStyleOrNone::Name(CustomIdent(match value {
                        % for style in values:
                        structs::NS_STYLE_LIST_STYLE_${style.replace('-', '_').upper()} => atom!("${style}"),
                        % endfor
                        _ => unreachable!("Unknown counter style keyword value"),
                    }))
                })
            }
        }

        #[inline]
        pub fn get_initial_value() -> computed_value::T {
            computed_value::T::CounterStyle(CounterStyleOrNone::disc())
        }

        #[inline]
        pub fn get_initial_specified_value() -> SpecifiedValue {
            SpecifiedValue::CounterStyle(CounterStyleOrNone::disc())
        }

        pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                             -> Result<SpecifiedValue, ParseError<'i>> {
            Ok(if let Ok(style) = input.try(|i| CounterStyleOrNone::parse(context, i)) {
                SpecifiedValue::CounterStyle(style)
            } else {
                SpecifiedValue::String(input.expect_string()?.as_ref().to_owned())
            })
        }
    </%helpers:longhand>
% endif

<%helpers:longhand name="list-style-image" animation_value_type="discrete"
                   boxed="${product == 'gecko'}"
                   spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image">
    use values::computed::ComputedValueAsSpecified;
    use values::specified::UrlOrNone;
    pub use self::computed_value::T as SpecifiedValue;

    pub mod computed_value {
        use values::specified::UrlOrNone;

        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        #[derive(Clone, Debug, PartialEq, ToCss)]
        pub struct T(pub UrlOrNone);
    }

    impl ComputedValueAsSpecified for SpecifiedValue {}

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T(Either::Second(None_))
    }
    #[inline]
    pub fn get_initial_specified_value() -> SpecifiedValue {
        SpecifiedValue(Either::Second(None_))
    }
    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue,ParseError<'i>> {
        % if product == "gecko":
        let mut value = input.try(|input| UrlOrNone::parse(context, input))?;
        if let Either::First(ref mut url) = value {
            url.build_image_value();
        }
        % else :
        let value = input.try(|input| UrlOrNone::parse(context, input))?;
        % endif

        return Ok(SpecifiedValue(value));
    }
</%helpers:longhand>

<%helpers:longhand name="quotes" animation_value_type="discrete"
                   spec="https://drafts.csswg.org/css-content/#propdef-quotes">
    use cssparser::serialize_string;
    use std::fmt;
    use style_traits::ToCss;

    pub use self::computed_value::T as SpecifiedValue;

    pub mod computed_value {
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        #[derive(Clone, Debug, PartialEq, ToComputedValue)]
        pub struct T(pub Vec<(String, String)>);
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.0.is_empty() {
                return dest.write_str("none")
            }

            let mut first = true;
            for pair in &self.0 {
                if !first {
                    dest.write_str(" ")?;
                }
                first = false;
                serialize_string(&*pair.0, dest)?;
                dest.write_str(" ")?;
                serialize_string(&*pair.1, dest)?;
            }
            Ok(())
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T(vec![
            ("\u{201c}".to_owned(), "\u{201d}".to_owned()),
            ("\u{2018}".to_owned(), "\u{2019}".to_owned()),
        ])
    }

    pub fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue,ParseError<'i>> {
        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(SpecifiedValue(Vec::new()))
        }

        let mut quotes = Vec::new();
        loop {
            let first = match input.next() {
                Ok(&Token::QuotedString(ref value)) => value.as_ref().to_owned(),
                Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
                Err(_) => break,
            };
            let second = match input.next() {
                Ok(&Token::QuotedString(ref value)) => value.as_ref().to_owned(),
                Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
                Err(e) => return Err(e.into()),
            };
            quotes.push((first, second))
        }
        if !quotes.is_empty() {
            Ok(SpecifiedValue(quotes))
        } else {
            Err(StyleParseError::UnspecifiedError.into())
        }
    }
</%helpers:longhand>

${helpers.predefined_type("-moz-image-region",
                          "ClipRectOrAuto",
                          "computed::ClipRectOrAuto::auto()",
                          animation_value_type="ComputedValue",
                          products="gecko",
                          boxed="True",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-image-region)")}
<|endoftext|>


<commit_before>//! This module defines the dynamically typed value type used by Lea

pub use self::Value::*;

use table::Table;
use array::Array;
use function::Function;
use mem::{TracedRef, Tracer};

use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};

/// Wrapper around `f64` that implements `Hash` and `Eq` (needed when used as table keys).
///
/// Lua supports this, so we will, too. I would still never recommend doing this!
#[derive(PartialEq, Debug)]
pub struct HashedFloat(pub f64);

/// Manual implementation needed since `f64` doesn't implement Hash, but this is required since
/// floats can be used as table keys (although I don't recommend it).
impl Hash for HashedFloat {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        state.write_i64(self.0 as i64);
    }
}

impl Eq for HashedFloat {}

impl Deref for HashedFloat {
    type Target = f64;

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

impl DerefMut for HashedFloat {
    fn deref_mut(&mut self) -> &mut f64 {
        &mut self.0
    }
}

#[derive(PartialEq, Eq, Debug, Hash)]
pub enum Value<'gc> {
    TNil,
    TBool(bool),
    TInt(i64),
    TFloat(HashedFloat),
    TStr(TracedRef<'gc, String>),
    TFunc(TracedRef<'gc, Function<'gc>>),
    TArray(TracedRef<'gc, Array<'gc>>),
    TTable(TracedRef<'gc, Table<'gc>>),
}

impl <'gc> Value<'gc> {
    /// The equivalent of Lua's `type()` function, returns the type name of a value.
    pub fn get_type_name(&self) -> &'static str {
        match *self {
            TNil => "nil",
            TBool(..) => "boolean",
            TInt(..) | TFloat(..) => "number",
            TStr(..) => "string",
            TTable(..) => "table",
            TArray(..) => "array",
            TFunc(..) => "function",
        }
    }

    /// If this `Value` references a GC-object, marks it using the given `Tracer`.
    pub fn trace<T: Tracer>(&self, t: &mut T) {
        match *self {
            TNil | TBool(_) | TInt(_) | TFloat(_) => {},
            TStr(r) => unsafe { t.mark_untraceable(r) },    // Safe, since T is String
            TFunc(r) => t.mark_traceable(r),
            TArray(r) => t.mark_traceable(r),
            TTable(r) => t.mark_traceable(r),
        }
    }
}
<commit_msg>Fix HashedFloat's Hash impl<commit_after>//! This module defines the dynamically typed value type used by Lea

pub use self::Value::*;

use table::Table;
use array::Array;
use function::Function;
use mem::{TracedRef, Tracer};

use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::mem::transmute;

/// Wrapper around `f64` that implements `Hash` and `Eq` (needed when used as table keys).
///
/// Lua supports this, so we will, too. I would still never recommend doing this!
#[derive(PartialEq, Debug)]
pub struct HashedFloat(pub f64);

/// Manual implementation needed since `f64` doesn't implement Hash, but this is required since
/// floats can be used as table keys (although I don't recommend it).
impl Hash for HashedFloat {
    fn hash<H: Hasher>(&self, state: &mut H) {
        // Just use the float's raw bits for the hash. This is safe.
        state.write_i64(unsafe { transmute(self.0) } );
    }
}

impl Eq for HashedFloat {}

impl Deref for HashedFloat {
    type Target = f64;

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

impl DerefMut for HashedFloat {
    fn deref_mut(&mut self) -> &mut f64 {
        &mut self.0
    }
}

#[derive(PartialEq, Eq, Debug, Hash)]
pub enum Value<'gc> {
    TNil,
    TBool(bool),
    TInt(i64),
    TFloat(HashedFloat),
    TStr(TracedRef<'gc, String>),
    TFunc(TracedRef<'gc, Function<'gc>>),
    TArray(TracedRef<'gc, Array<'gc>>),
    TTable(TracedRef<'gc, Table<'gc>>),
}

impl <'gc> Value<'gc> {
    /// The equivalent of Lua's `type()` function, returns the type name of a value.
    pub fn get_type_name(&self) -> &'static str {
        match *self {
            TNil => "nil",
            TBool(..) => "boolean",
            TInt(..) | TFloat(..) => "number",
            TStr(..) => "string",
            TTable(..) => "table",
            TArray(..) => "array",
            TFunc(..) => "function",
        }
    }

    /// If this `Value` references a GC-object, marks it using the given `Tracer`.
    pub fn trace<T: Tracer>(&self, t: &mut T) {
        match *self {
            TNil | TBool(_) | TInt(_) | TFloat(_) => {},
            TStr(r) => unsafe { t.mark_untraceable(r) },    // Safe, since T is String
            TFunc(r) => t.mark_traceable(r),
            TArray(r) => t.mark_traceable(r),
            TTable(r) => t.mark_traceable(r),
        }
    }
}
<|endoftext|>