<commit_before>mod global;
mod handlers;
mod tree;
mod region;

pub use self::tree::RoleStatus;
use self::tree::SurfaceData;
use wayland_server::{Client, EventLoopHandle, Init, resource_is_registered};

use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_surface};

/// Description of which part of a surface
/// should be considered damaged and needs to be redrawn
pub enum Damage {
    /// The whole surface must be considered damaged (this is the default)
    Full,
    /// A rectangle containing the damaged zone, in surface coordinates
    Surface(Rectangle),
    /// A rectangle containing the smaazed zone, in buffer coordinates
    ///
    /// Note: Buffer scaling must be taken into consideration
    Buffer(Rectangle),
}

/// Data associated with a surface, aggreged by the handlers
///
/// Most of the fields of this struct represent a double-buffered state, which
/// should only be applied once a `commit` request is received from the surface.
///
/// You are responsible for setting those values as you see fit to avoid
/// processing them two times.
pub struct SurfaceAttributes<U> {
    /// Buffer defining the contents of the surface
    ///
    /// The tuple represent the coordinates of this buffer
    /// relative to the location of the current buffer.
    ///
    /// If set to `Some(None)`, it means the user specifically asked for the
    /// surface to be unmapped.
    ///
    /// You are free to set this field to `None` to avoid processing it several
    /// times. It'll be set to `Some(...)` if the user attaches a buffer (or NULL) to
    /// the surface.
    pub buffer: Option<Option<(wl_buffer::WlBuffer, (i32, i32))>>,
    /// Scale of the contents of the buffer, for higher-resolution contents.
    ///
    /// If it matches the one of the output displaying this surface, no change
    /// is necessary.
    pub buffer_scale: i32,
    /// Transform under which interpret the contents of the buffer
    ///
    /// If it matches the one of the output displaying this surface, no change
    /// is necessary.
    pub buffer_transform: wl_output::Transform,
    /// Region of the surface that is guaranteed to be opaque
    ///
    /// By default the whole surface is potentially transparent
    pub opaque_region: Option<RegionAttributes>,
    /// Region of the surface that is sensitive to user input
    ///
    /// By default the whole surface should be sensitive
    pub input_region: Option<RegionAttributes>,
    /// Damage rectangle
    ///
    /// Hint provided by the client to suggest that only this part
    /// of the surface was changed and needs to be redrawn
    pub damage: Damage,
    /// Subsurface-related attribute
    ///
    /// Is `Some` if this surface is a sub-surface
    ///
    /// **Warning:** Changing this field by yourself can cause panics.
    pub subsurface_attributes: Option<SubsurfaceAttributes>,
    /// User-controlled data
    ///
    /// This is your field to host whatever you need.
    pub user_data: U,
}

impl<U: Default> Default for SurfaceAttributes<U> {
    fn default() -> SurfaceAttributes<U> {
        SurfaceAttributes {
            buffer: None,
            buffer_scale: 1,
            buffer_transform: wl_output::Transform::Normal,
            opaque_region: None,
            input_region: None,
            damage: Damage::Full,
            subsurface_attributes: None,
            user_data: Default::default(),
        }
    }
}

/// Attributes defining the behaviour of a sub-surface relative to its parent
pub struct SubsurfaceAttributes {
    /// Horizontal location of the top-left corner of this sub-surface relative to
    /// the top-left corner of its parent
    pub x: i32,
    /// Vertical location of the top-left corner of this sub-surface relative to
    /// the top-left corner of its parent
    pub y: i32,
    /// Sync status of this sub-surface
    ///
    /// If `true`, this surface should be repainted synchronously with its parent
    /// if `false`, it should be considered independant of its parent regarding
    /// repaint timings.
    pub sync: bool,
}

impl Default for SubsurfaceAttributes {
    fn default() -> SubsurfaceAttributes {
        SubsurfaceAttributes {
            x: 0,
            y: 0,
            sync: true,
        }
    }
}

/// Kind of a rectangle part of a region
#[derive(Copy,Clone)]
pub enum RectangleKind {
    /// This rectangle should be added to the region
    Add,
    /// The intersection of this rectangle with the region should
    /// be removed from the region
    Subtract,
}

/// A rectangle defined by its top-left corner and dimensions
#[derive(Copy,Clone)]
pub struct Rectangle {
    /// horizontal position of the top-leftcorner of the rectangle, in surface coordinates
    pub x: i32,
    /// vertical position of the top-leftcorner of the rectangle, in surface coordinates
    pub y: i32,
    /// width of the rectangle
    pub width: i32,
    /// height of the rectangle
    pub height: i32,
}

/// Description of the contents of a region
///
/// A region is defined as an union and difference of rectangle.
///
/// This struct contains an ordered Vec containing the rectangles defining
/// a region. They should be added or substracted in this order to compute the
/// actual contents of the region.
#[derive(Clone)]
pub struct RegionAttributes {
    /// List of rectangle part of this region
    pub rects: Vec<(RectangleKind, Rectangle)>,
}

impl Default for RegionAttributes {
    fn default() -> RegionAttributes {
        RegionAttributes { rects: Vec::new() }
    }
}

/// A Compositor global token
///
/// This token can be cloned at will, and is the entry-point to
/// access data associated with the wl_surface and wl_region managed
/// by the `CompositorGlobal` that provided it.
#[derive(Copy,Clone)]
pub struct CompositorToken<U, H> {
    hid: usize,
    _data: ::std::marker::PhantomData<*mut U>,
    _handler: ::std::marker::PhantomData<*mut H>,
}

impl<U: Send + 'static, H: Handler + Send + 'static> CompositorToken<U, H> {
    /// Access the data of a surface
    ///
    /// The closure will be called with the contents of the data associated with this surface.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn with_surface_data<F>(&self, surface: &wl_surface::WlSurface, f: F)
        where F: FnOnce(&mut SurfaceAttributes<U>)
    {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe {
            SurfaceData::<U>::with_data(surface, f);
        }
    }

    /// Access the data of a surface tree
    ///
    /// The provided closure is called successively on the surface and all its child subsurfaces,
    /// in a depth-first order. This matches the order in which the surfaces are supposed to be
    /// drawn: top-most last.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn with_surface_tree<F>(&self, surface: &wl_surface::WlSurface, f: F) -> Result<(), ()>
        where F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>) -> bool
    {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe {
            SurfaceData::<U>::map_tree(surface, f);
        }
        Ok(())
    }

    /// Retrieve the parent of this surface
    ///
    /// Returns `None` is this surface is a root surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_parent(&self, surface: &wl_surface::WlSurface) -> Option<wl_surface::WlSurface> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::get_parent(surface) }
    }

    /// Retrieve the children of this surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_children(&self, surface: &wl_surface::WlSurface) -> Vec<wl_surface::WlSurface> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::get_children(surface) }
    }

    /// Retrieve the role status this surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn role_status(&self, surface: &wl_surface::WlSurface) -> RoleStatus {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::role_status(surface) }
    }

    /// Register that this surface has a role
    ///
    /// This makes this surface impossible to become a subsurface, as
    /// a surface can only have a single role at a time.
    ///
    /// Fails if the surface already has a role.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn give_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::give_role(surface) }
    }

    /// Register that this surface has no role
    ///
    /// It is a noop if this surface already didn't have one, but fails if
    /// the role was "subsurface". This role is automatically managed and as such
    /// cannot be removed manually.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn remove_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::remove_role(surface) }
    }

    /// Retrieve the metadata associated with a wl_region
    ///
    /// If the region is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_region_attributes(&self, region: &wl_region::WlRegion) -> RegionAttributes {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(region, self.hid),
                "Accessing the data of foreign regions is not supported.");
        unsafe { RegionData::get_attributes(region) }
    }
}

pub struct CompositorHandler<U, H> {
    my_id: usize,
    log: ::slog::Logger,
    handler: H,
    _data: ::std::marker::PhantomData<U>,
}

impl<U, H> Init for CompositorHandler<U, H> {
    fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) {
        self.my_id = index;
        debug!(self.log, "Init finished")
    }
}

impl<U, H> CompositorHandler<U, H> {
    /// Create a new CompositorHandler
    pub fn new<L>(handler: H, logger: L) -> CompositorHandler<U, H>
        where L: Into<Option<::slog::Logger>>
    {
        let log = ::slog_or_stdlog(logger);
        CompositorHandler {
            my_id: ::std::usize::MAX,
            log: log.new(o!("smithay_module" => "compositor_handler")),
            handler: handler,
            _data: ::std::marker::PhantomData,
        }
    }

    /// Create a token to access the data associated to the objects managed by this handler.
    pub fn get_token(&self) -> CompositorToken<U, H> {
        assert!(self.my_id != ::std::usize::MAX,
                "CompositorHandler is not initialized yet.");
        CompositorToken {
            hid: self.my_id,
            _data: ::std::marker::PhantomData,
            _handler: ::std::marker::PhantomData,
        }
    }

    /// Access the underlying sub-handler
    pub fn get_handler(&mut self) -> &mut H {
        &mut self.handler
    }
}

/// Sub-handler trait for surface event handling
///
/// The global provided by Smithay cannot process these events for you, so they
/// are forwarded directly to a handler implementing this trait that you must provide
/// at creation of the `CompositorHandler`.
pub trait Handler {
    /// See `wayland_server::protocol::wl_surface::Handler::commit`
    fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface);
    /// See `wayland_server::protocol::wl_surface::Handler::frame`
    fn frame(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface,
             callback: wl_callback::WlCallback);
}
<commit_msg>compositor: provide default noop impls from Handler<commit_after>mod global;
mod handlers;
mod tree;
mod region;

pub use self::tree::RoleStatus;
use self::tree::SurfaceData;
use wayland_server::{Client, EventLoopHandle, Init, resource_is_registered};

use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_surface};

/// Description of which part of a surface
/// should be considered damaged and needs to be redrawn
pub enum Damage {
    /// The whole surface must be considered damaged (this is the default)
    Full,
    /// A rectangle containing the damaged zone, in surface coordinates
    Surface(Rectangle),
    /// A rectangle containing the smaazed zone, in buffer coordinates
    ///
    /// Note: Buffer scaling must be taken into consideration
    Buffer(Rectangle),
}

/// Data associated with a surface, aggreged by the handlers
///
/// Most of the fields of this struct represent a double-buffered state, which
/// should only be applied once a `commit` request is received from the surface.
///
/// You are responsible for setting those values as you see fit to avoid
/// processing them two times.
pub struct SurfaceAttributes<U> {
    /// Buffer defining the contents of the surface
    ///
    /// The tuple represent the coordinates of this buffer
    /// relative to the location of the current buffer.
    ///
    /// If set to `Some(None)`, it means the user specifically asked for the
    /// surface to be unmapped.
    ///
    /// You are free to set this field to `None` to avoid processing it several
    /// times. It'll be set to `Some(...)` if the user attaches a buffer (or NULL) to
    /// the surface.
    pub buffer: Option<Option<(wl_buffer::WlBuffer, (i32, i32))>>,
    /// Scale of the contents of the buffer, for higher-resolution contents.
    ///
    /// If it matches the one of the output displaying this surface, no change
    /// is necessary.
    pub buffer_scale: i32,
    /// Transform under which interpret the contents of the buffer
    ///
    /// If it matches the one of the output displaying this surface, no change
    /// is necessary.
    pub buffer_transform: wl_output::Transform,
    /// Region of the surface that is guaranteed to be opaque
    ///
    /// By default the whole surface is potentially transparent
    pub opaque_region: Option<RegionAttributes>,
    /// Region of the surface that is sensitive to user input
    ///
    /// By default the whole surface should be sensitive
    pub input_region: Option<RegionAttributes>,
    /// Damage rectangle
    ///
    /// Hint provided by the client to suggest that only this part
    /// of the surface was changed and needs to be redrawn
    pub damage: Damage,
    /// Subsurface-related attribute
    ///
    /// Is `Some` if this surface is a sub-surface
    ///
    /// **Warning:** Changing this field by yourself can cause panics.
    pub subsurface_attributes: Option<SubsurfaceAttributes>,
    /// User-controlled data
    ///
    /// This is your field to host whatever you need.
    pub user_data: U,
}

impl<U: Default> Default for SurfaceAttributes<U> {
    fn default() -> SurfaceAttributes<U> {
        SurfaceAttributes {
            buffer: None,
            buffer_scale: 1,
            buffer_transform: wl_output::Transform::Normal,
            opaque_region: None,
            input_region: None,
            damage: Damage::Full,
            subsurface_attributes: None,
            user_data: Default::default(),
        }
    }
}

/// Attributes defining the behaviour of a sub-surface relative to its parent
pub struct SubsurfaceAttributes {
    /// Horizontal location of the top-left corner of this sub-surface relative to
    /// the top-left corner of its parent
    pub x: i32,
    /// Vertical location of the top-left corner of this sub-surface relative to
    /// the top-left corner of its parent
    pub y: i32,
    /// Sync status of this sub-surface
    ///
    /// If `true`, this surface should be repainted synchronously with its parent
    /// if `false`, it should be considered independant of its parent regarding
    /// repaint timings.
    pub sync: bool,
}

impl Default for SubsurfaceAttributes {
    fn default() -> SubsurfaceAttributes {
        SubsurfaceAttributes {
            x: 0,
            y: 0,
            sync: true,
        }
    }
}

/// Kind of a rectangle part of a region
#[derive(Copy,Clone)]
pub enum RectangleKind {
    /// This rectangle should be added to the region
    Add,
    /// The intersection of this rectangle with the region should
    /// be removed from the region
    Subtract,
}

/// A rectangle defined by its top-left corner and dimensions
#[derive(Copy,Clone)]
pub struct Rectangle {
    /// horizontal position of the top-leftcorner of the rectangle, in surface coordinates
    pub x: i32,
    /// vertical position of the top-leftcorner of the rectangle, in surface coordinates
    pub y: i32,
    /// width of the rectangle
    pub width: i32,
    /// height of the rectangle
    pub height: i32,
}

/// Description of the contents of a region
///
/// A region is defined as an union and difference of rectangle.
///
/// This struct contains an ordered Vec containing the rectangles defining
/// a region. They should be added or substracted in this order to compute the
/// actual contents of the region.
#[derive(Clone)]
pub struct RegionAttributes {
    /// List of rectangle part of this region
    pub rects: Vec<(RectangleKind, Rectangle)>,
}

impl Default for RegionAttributes {
    fn default() -> RegionAttributes {
        RegionAttributes { rects: Vec::new() }
    }
}

/// A Compositor global token
///
/// This token can be cloned at will, and is the entry-point to
/// access data associated with the wl_surface and wl_region managed
/// by the `CompositorGlobal` that provided it.
#[derive(Copy,Clone)]
pub struct CompositorToken<U, H> {
    hid: usize,
    _data: ::std::marker::PhantomData<*mut U>,
    _handler: ::std::marker::PhantomData<*mut H>,
}

impl<U: Send + 'static, H: Handler + Send + 'static> CompositorToken<U, H> {
    /// Access the data of a surface
    ///
    /// The closure will be called with the contents of the data associated with this surface.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn with_surface_data<F>(&self, surface: &wl_surface::WlSurface, f: F)
        where F: FnOnce(&mut SurfaceAttributes<U>)
    {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe {
            SurfaceData::<U>::with_data(surface, f);
        }
    }

    /// Access the data of a surface tree
    ///
    /// The provided closure is called successively on the surface and all its child subsurfaces,
    /// in a depth-first order. This matches the order in which the surfaces are supposed to be
    /// drawn: top-most last.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn with_surface_tree<F>(&self, surface: &wl_surface::WlSurface, f: F) -> Result<(), ()>
        where F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>) -> bool
    {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe {
            SurfaceData::<U>::map_tree(surface, f);
        }
        Ok(())
    }

    /// Retrieve the parent of this surface
    ///
    /// Returns `None` is this surface is a root surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_parent(&self, surface: &wl_surface::WlSurface) -> Option<wl_surface::WlSurface> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::get_parent(surface) }
    }

    /// Retrieve the children of this surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_children(&self, surface: &wl_surface::WlSurface) -> Vec<wl_surface::WlSurface> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::get_children(surface) }
    }

    /// Retrieve the role status this surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn role_status(&self, surface: &wl_surface::WlSurface) -> RoleStatus {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::role_status(surface) }
    }

    /// Register that this surface has a role
    ///
    /// This makes this surface impossible to become a subsurface, as
    /// a surface can only have a single role at a time.
    ///
    /// Fails if the surface already has a role.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn give_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::give_role(surface) }
    }

    /// Register that this surface has no role
    ///
    /// It is a noop if this surface already didn't have one, but fails if
    /// the role was "subsurface". This role is automatically managed and as such
    /// cannot be removed manually.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn remove_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::remove_role(surface) }
    }

    /// Retrieve the metadata associated with a wl_region
    ///
    /// If the region is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_region_attributes(&self, region: &wl_region::WlRegion) -> RegionAttributes {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(region, self.hid),
                "Accessing the data of foreign regions is not supported.");
        unsafe { RegionData::get_attributes(region) }
    }
}

pub struct CompositorHandler<U, H> {
    my_id: usize,
    log: ::slog::Logger,
    handler: H,
    _data: ::std::marker::PhantomData<U>,
}

impl<U, H> Init for CompositorHandler<U, H> {
    fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) {
        self.my_id = index;
        debug!(self.log, "Init finished")
    }
}

impl<U, H> CompositorHandler<U, H> {
    /// Create a new CompositorHandler
    pub fn new<L>(handler: H, logger: L) -> CompositorHandler<U, H>
        where L: Into<Option<::slog::Logger>>
    {
        let log = ::slog_or_stdlog(logger);
        CompositorHandler {
            my_id: ::std::usize::MAX,
            log: log.new(o!("smithay_module" => "compositor_handler")),
            handler: handler,
            _data: ::std::marker::PhantomData,
        }
    }

    /// Create a token to access the data associated to the objects managed by this handler.
    pub fn get_token(&self) -> CompositorToken<U, H> {
        assert!(self.my_id != ::std::usize::MAX,
                "CompositorHandler is not initialized yet.");
        CompositorToken {
            hid: self.my_id,
            _data: ::std::marker::PhantomData,
            _handler: ::std::marker::PhantomData,
        }
    }

    /// Access the underlying sub-handler
    pub fn get_handler(&mut self) -> &mut H {
        &mut self.handler
    }
}

/// Sub-handler trait for surface event handling
///
/// The global provided by Smithay cannot process these events for you, so they
/// are forwarded directly to a handler implementing this trait that you must provide
/// at creation of the `CompositorHandler`.
#[allow(unused_variables)]
pub trait Handler {
    /// See `wayland_server::protocol::wl_surface::Handler::commit`
    fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface) {}
    /// See `wayland_server::protocol::wl_surface::Handler::frame`
    fn frame(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface,
             callback: wl_callback::WlCallback) {
    }
}
<|endoftext|>


<commit_before>use types::NumPlies;

use super::color::{White, Black};
use super::piece::{Piece, WK, WR, BK, BR, Pawn};
use super::square::{Square,File,Rank};
use super::moves::Move;
use super::pos::Position;
use super::castle::{Kingside,Queenside};

pub fn make_move(p: &mut Position, m: &Move) {
    let from = m.from();
    let to = m.to();
    let curr_piece = p.at(from).unwrap();
    let curr_color = p.side_to_move();

    if m.capture_normal().is_some() || m.is_en_passant() || curr_piece.piece_type() == Pawn {
        p.set_ply_count(NumPlies(0));
    } else {
        let NumPlies(temp) = p.ply_count();
        p.set_ply_count(NumPlies(temp+1));
    }

    if let Some(castle_side) = m.castle() {

        //no en passant
        p.set_en_passant(None);

        let (rook_from, rook_to) = match (curr_color, castle_side) {
            (White, Kingside ) => (Square::new(File(7),Rank(0)), Square::new(File(5),Rank(0))),
            (White, Queenside) => (Square::new(File(0),Rank(0)), Square::new(File(3),Rank(0))),
            (Black, Kingside ) => (Square::new(File(7),Rank(7)), Square::new(File(5),Rank(7))),
            (Black, Queenside) => (Square::new(File(0),Rank(7)), Square::new(File(3),Rank(7))),
        };

        p.remove_at(from, curr_piece);
        p.set_at(to, curr_piece);
        let curr_rook = p.at(rook_from).unwrap();
        p.remove_at(rook_from, curr_rook);
        p.set_at(rook_to, curr_rook);

    } else if m.is_en_passant() {

        //no en passant after this
        p.set_en_passant(None);

        let captured = Square::new(to.file(), from.rank());
        let captured_piece = Piece::new(p.side_to_move().invert(), Pawn);

        p.remove_at(captured, captured_piece);
        p.remove_at(from, curr_piece);
        p.set_at(to, curr_piece);

    } else if let Some(promote_piece) = m.promote() {
        //no en passant
        p.set_en_passant(None);

        //change the board and promote
        if let Some(captured_piece) = m.capture_normal() {
            p.remove_at(to, captured_piece);
        }
        p.remove_at(from, curr_piece);
        p.set_at(to, Piece::new(curr_color, promote_piece));
    } else {

        if m.is_pawn_double_move() {
            p.set_en_passant(Some(to.file()));
        } else {
            p.set_en_passant(None);
        }

        //change the board
        if let Some(captured_piece) = m.capture_normal() {
            p.remove_at(to, captured_piece);
        }
        p.remove_at(from, curr_piece);
        p.set_at(to, curr_piece);
    }

    //castling
    if p.at(Square::new(File(4),Rank(0))) != Some(WK) {
        p.set_castle(Queenside, White, false);
        p.set_castle(Kingside , White, false);
    }
    if p.at(Square::new(File(0),Rank(0))) != Some(WR) {
        p.set_castle(Queenside, White, false);
    }
    if p.at(Square::new(File(7),Rank(0))) != Some(WR) {
        p.set_castle(Kingside , White, false);
    }
    if p.at(Square::new(File(4),Rank(7))) != Some(BK) {
        p.set_castle(Queenside, Black, false);
        p.set_castle(Kingside , Black, false);
    }
    if p.at(Square::new(File(0),Rank(7))) != Some(BR) {
        p.set_castle(Queenside, Black, false);
    }
    if p.at(Square::new(File(7),Rank(7))) != Some(BR) {
        p.set_castle(Kingside , Black, false);
    }

    p.swap_side_to_move();
}
<commit_msg>Implement unmake_move()<commit_after>use types::NumPlies;

use super::color::{White, Black};
use super::piece::{Piece, WK, WR, BK, BR, Pawn};
use super::square::{Square,File,Rank};
use super::moves::Move;
use super::pos::{self, Position};
use super::castle::{Kingside,Queenside};

pub fn make_move(p: &mut Position, m: &Move) {
    let from = m.from();
    let to = m.to();
    let curr_piece = p.at(from).unwrap();
    let curr_color = p.side_to_move();

    if m.capture_normal().is_some() || m.is_en_passant() || curr_piece.piece_type() == Pawn {
        p.set_ply_count(NumPlies(0));
    } else {
        let NumPlies(temp) = p.ply_count();
        p.set_ply_count(NumPlies(temp+1));
    }

    if let Some(castle_side) = m.castle() {

        //no en passant
        p.set_en_passant(None);

        let (rook_from, rook_to) = match (curr_color, castle_side) {
            (White, Kingside ) => (Square::new(File(7),Rank(0)), Square::new(File(5),Rank(0))),
            (White, Queenside) => (Square::new(File(0),Rank(0)), Square::new(File(3),Rank(0))),
            (Black, Kingside ) => (Square::new(File(7),Rank(7)), Square::new(File(5),Rank(7))),
            (Black, Queenside) => (Square::new(File(0),Rank(7)), Square::new(File(3),Rank(7))),
        };

        p.remove_at(from, curr_piece);
        p.set_at(to, curr_piece);
        let curr_rook = p.at(rook_from).unwrap();
        p.remove_at(rook_from, curr_rook);
        p.set_at(rook_to, curr_rook);

    } else if m.is_en_passant() {

        //no en passant after this
        p.set_en_passant(None);

        let captured = Square::new(to.file(), from.rank());
        let captured_piece = Piece::new(p.side_to_move().invert(), Pawn);

        p.remove_at(captured, captured_piece);
        p.remove_at(from, curr_piece);
        p.set_at(to, curr_piece);

    } else if let Some(promote_piece) = m.promote() {
        //no en passant
        p.set_en_passant(None);

        //change the board and promote
        if let Some(captured_piece) = m.capture_normal() {
            p.remove_at(to, captured_piece);
        }
        p.remove_at(from, curr_piece);
        p.set_at(to, Piece::new(curr_color, promote_piece));
    } else {

        if m.is_pawn_double_move() {
            p.set_en_passant(Some(to.file()));
        } else {
            p.set_en_passant(None);
        }

        //change the board
        if let Some(captured_piece) = m.capture_normal() {
            p.remove_at(to, captured_piece);
        }
        p.remove_at(from, curr_piece);
        p.set_at(to, curr_piece);
    }

    //castling
    if p.at(Square::new(File(4),Rank(0))) != Some(WK) {
        p.set_castle(Queenside, White, false);
        p.set_castle(Kingside , White, false);
    }
    if p.at(Square::new(File(0),Rank(0))) != Some(WR) {
        p.set_castle(Queenside, White, false);
    }
    if p.at(Square::new(File(7),Rank(0))) != Some(WR) {
        p.set_castle(Kingside , White, false);
    }
    if p.at(Square::new(File(4),Rank(7))) != Some(BK) {
        p.set_castle(Queenside, Black, false);
        p.set_castle(Kingside , Black, false);
    }
    if p.at(Square::new(File(0),Rank(7))) != Some(BR) {
        p.set_castle(Queenside, Black, false);
    }
    if p.at(Square::new(File(7),Rank(7))) != Some(BR) {
        p.set_castle(Kingside , Black, false);
    }

    p.swap_side_to_move();
}

pub fn unmake_move(p: &mut Position, m: &Move, extra_data: pos::ExtraData) {
    let from = m.from();
    let to = m.to();
    let curr_piece = p.at(to).unwrap();
    let curr_color = p.side_to_move().invert();

    if let Some(castle_side) = m.castle() {

        let (rook_from, rook_to) = match (curr_color, castle_side) {
            (White, Kingside ) => (Square::new(File(7),Rank(0)), Square::new(File(5),Rank(0))),
            (White, Queenside) => (Square::new(File(0),Rank(0)), Square::new(File(3),Rank(0))),
            (Black, Kingside ) => (Square::new(File(7),Rank(7)), Square::new(File(5),Rank(7))),
            (Black, Queenside) => (Square::new(File(0),Rank(7)), Square::new(File(3),Rank(7))),
        };

        p.remove_at(to, curr_piece);
        p.set_at(from, curr_piece);
        let curr_rook = p.at(rook_to).unwrap();
        p.remove_at(rook_to, curr_rook);
        p.set_at(rook_from, curr_rook);

    } else if m.is_en_passant() {

        let captured = Square::new(to.file(), from.rank());
        let captured_piece = Piece::new(curr_color.invert(), Pawn);

        p.set_at(captured, captured_piece);
        p.remove_at(to, curr_piece);
        p.set_at(from, curr_piece);

    } else if let Some(promote_piece) = m.promote() {

        p.remove_at(to, Piece::new(curr_color, promote_piece));
        p.set_at(to, curr_piece);
        if let Some(captured_piece) = m.capture_normal() {
            p.set_at(to, captured_piece);
        }

    } else {

        p.remove_at(to, curr_piece);
        p.set_at(from, curr_piece);
        if let Some(captured_piece) = m.capture_normal() {
            p.set_at(to, captured_piece);
        }

    }

    p.set_extra_data(extra_data);
    p.swap_side_to_move();
}
<|endoftext|>


<commit_before>#![allow(unused_variables, dead_code)]

use std::ffi::{OsStr, OsString};

pub type ShortArg = u8;
pub type LongArg = &'static str;

#[derive(PartialEq, Debug)]
pub enum Flag {
    Short(ShortArg),
    Long(LongArg),
}

#[derive(PartialEq, Debug)]
pub enum Strictness {
    ComplainAboutRedundantArguments,
    UseLastArguments,
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum TakesValue {
    Necessary,
    Forbidden,
}

#[derive(PartialEq, Debug)]
pub struct Arg {
    short: Option<ShortArg>,
    long: LongArg,
    takes_value: TakesValue,
}

#[derive(PartialEq, Debug)]
pub struct Args(&'static [Arg]);

impl Args {
    fn lookup_short<'a>(&self, short: ShortArg) -> Result<&Arg, ParseError<'a>> {
        match self.0.into_iter().find(|arg| arg.short == Some(short)) {
            Some(arg)  => Ok(arg),
            None       => Err(ParseError::UnknownShortArgument { attempt: short })
        }
    }

    fn lookup_long<'a>(&self, long: &'a OsStr) -> Result<&Arg, ParseError<'a>> {
        match self.0.into_iter().find(|arg| arg.long == long) {
            Some(arg)  => Ok(arg),
            None       => Err(ParseError::UnknownArgument { attempt: long })
        }
    }
}

#[derive(PartialEq, Debug)]
pub struct Matches<'a> {
    /// Long and short arguments need to be kept in the same vector, because
    /// we usually want the one nearest the end to count.
    flags: Vec<(Flag, Option<&'a OsStr>)>,
    frees: Vec<&'a OsStr>,
}

#[derive(PartialEq, Debug)]
pub enum ParseError<'a> {
    NeedsValue { flag: Flag },
    ForbiddenValue { flag: Flag },
    UnknownShortArgument { attempt: ShortArg },
    UnknownArgument { attempt: &'a OsStr },
}

fn parse<'a>(args: Args, inputs: &'a [OsString]) -> Result<Matches<'a>, ParseError<'a>> {
    use std::os::unix::ffi::OsStrExt;
    use self::TakesValue::*;

    let mut parsing = true;

    let mut results = Matches {
        flags: Vec::new(),
        frees: Vec::new(),
    };

    let mut iter = inputs.iter();
    while let Some(arg) = iter.next() {
        let bytes = arg.as_bytes();

        if !parsing {
            results.frees.push(arg)
        }
        else if arg == "--" {
            parsing = false;
        }
        else if bytes.starts_with(b"--") {
            let long_arg_name = OsStr::from_bytes(&bytes[2..]);

            if let Some((before, after)) = split_on_equals(long_arg_name) {
                let arg = args.lookup_long(before)?;
                let flag = Flag::Long(arg.long);
                match arg.takes_value {
                    Necessary  => results.flags.push((flag, Some(after))),
                    Forbidden  => return Err(ParseError::ForbiddenValue { flag })
                }
            }
            else {
                let arg = args.lookup_long(long_arg_name)?;
                let flag = Flag::Long(arg.long);
                match arg.takes_value {
                    Forbidden  => results.flags.push((flag, None)),
                    Necessary  => {
                        if let Some(next_arg) = iter.next() {
                            results.flags.push((flag, Some(next_arg)));
                        }
                        else {
                            return Err(ParseError::NeedsValue { flag })
                        }
                    }
                }
            }
        }
        else if bytes.starts_with(b"-") && arg != "-" {
            let short_arg = OsStr::from_bytes(&bytes[1..]);
            if let Some((before, after)) = split_on_equals(short_arg) {
                let (arg_with_value, other_args) = before.as_bytes().split_last().unwrap();

                for byte in other_args {
                    let arg = args.lookup_short(*byte)?;
                    let flag = Flag::Short(*byte);
                    match arg.takes_value {
                        Forbidden  => results.flags.push((flag, None)),
                        Necessary  => return Err(ParseError::NeedsValue { flag })
                    }
                }

                let arg = args.lookup_short(*before.as_bytes().last().unwrap())?;
                let flag = Flag::Short(arg.short.unwrap());
                match arg.takes_value {
                    Necessary  => results.flags.push((flag, Some(after))),
                    Forbidden  => return Err(ParseError::ForbiddenValue { flag })
                }
            }
            else {
                for (index, byte) in bytes.into_iter().enumerate().skip(1) {
                    let arg = args.lookup_short(*byte)?;
                    let flag = Flag::Short(*byte);
                    match arg.takes_value {
                        Forbidden  => results.flags.push((flag, None)),
                        Necessary  => {
                            if index < bytes.len() - 1 {
                                let remnants = &bytes[index+1 ..];
                                results.flags.push((flag, Some(OsStr::from_bytes(remnants))));
                                break;
                            }
                            else if let Some(next_arg) = iter.next() {
                                results.flags.push((flag, Some(next_arg)));
                            }
                            else {
                                return Err(ParseError::NeedsValue { flag })
                            }
                        }
                    }
                }
            }
        }
        else {
            results.frees.push(arg)
        }
    }

    Ok(results)
}

/// Splits a string on its `=` character, returning the two substrings on
/// either side. Returns `None` if there’s no equals or a string is missing.
fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> {
    use std::os::unix::ffi::OsStrExt;

    if let Some(index) = input.as_bytes().iter().position(|elem| *elem == b'=') {
        let (before, after) = input.as_bytes().split_at(index);

        // The after string contains the = that we need to remove.
        if before.len() >= 1 && after.len() >= 2 {
            return Some((OsStr::from_bytes(before),
                         OsStr::from_bytes(&after[1..])))
        }
    }

    None
}

/// Creates an `OSString` (used in tests)
#[cfg(test)]
fn os(input: &'static str) -> OsString {
    let mut os = OsString::new();
    os.push(input);
    os
}

#[cfg(test)]
mod split_test {
    use super::{split_on_equals, os};

    macro_rules! test_split {
        ($name:ident: $input:expr => None) => {
            #[test]
            fn $name() {
                assert_eq!(split_on_equals(&os($input)),
                           None);
            }
        };

        ($name:ident: $input:expr => $before:expr, $after:expr) => {
            #[test]
            fn $name() {
                assert_eq!(split_on_equals(&os($input)),
                           Some((&*os($before), &*os($after))));
            }
        };
    }

    test_split!(empty:   ""   => None);
    test_split!(letter:  "a"  => None);

    test_split!(just:      "="    => None);
    test_split!(intro:     "=bbb" => None);
    test_split!(denou:  "aaa="    => None);
    test_split!(equals: "aaa=bbb" => "aaa", "bbb");

    test_split!(sort: "--sort=size"     => "--sort", "size");
    test_split!(more: "this=that=other" => "this",   "that=other");
}

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

    static TEST_ARGS: &'static [Arg] = &[
        Arg { short: Some(b'l'), long: "long",     takes_value: TakesValue::Forbidden },
        Arg { short: Some(b'v'), long: "verbose",  takes_value: TakesValue::Forbidden },
        Arg { short: Some(b'c'), long: "count",    takes_value: TakesValue::Necessary }
    ];

    macro_rules! test {
        ($name:ident: $input:expr => $result:expr) => {
            #[test]
            fn $name() {
                let bits = $input;
                let results = parse(Args(TEST_ARGS), &bits);
                assert_eq!(results, $result);
            }
        };
    }

    // Just filenames
    test!(empty:       []           => Ok(Matches { frees: vec![], flags: vec![] }));
    test!(one_arg:     [os("exa")]  => Ok(Matches { frees: vec![ os("exa").as_os_str() ], flags: vec![] }));

    // Dashes and double dashes
    test!(one_dash:    [os("-")]                 => Ok(Matches { frees: vec![ os("-").as_os_str() ],      flags: vec![] }));
    test!(two_dashes:  [os("--")]                => Ok(Matches { frees: vec![],                           flags: vec![] }));
    test!(two_file:    [os("--"), os("file")]    => Ok(Matches { frees: vec![ os("file").as_os_str() ],   flags: vec![] }));
    test!(two_arg_l:   [os("--"), os("--long")]  => Ok(Matches { frees: vec![ os("--long").as_os_str() ], flags: vec![] }));
    test!(two_arg_s:   [os("--"), os("-l")]      => Ok(Matches { frees: vec![ os("-l").as_os_str() ],     flags: vec![] }));

    // Long args
    test!(long:        [os("--long")]                  => Ok(Matches { frees: vec![],                      flags: vec![ (Flag::Long("long"), None) ] }));
    test!(long_then:   [os("--long"), os("4")]         => Ok(Matches { frees: vec![ os("4").as_os_str() ], flags: vec![ (Flag::Long("long"), None) ] }));
    test!(long_two:    [os("--long"), os("--verbose")] => Ok(Matches { frees: vec![],                      flags: vec![ (Flag::Long("long"), None), (Flag::Long("verbose"), None) ] }));

    // Long args with values
    test!(bad_equals:  [os("--long=equals")]    => Err(ParseError::ForbiddenValue { flag: Flag::Long("long") }));
    test!(no_arg:      [os("--count")]          => Err(ParseError::NeedsValue     { flag: Flag::Long("count") }));
    test!(arg_equals:  [os("--count=4")]        => Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("count"), Some(os("4").as_os_str())) ] }));
    test!(arg_then:    [os("--count"), os("4")] => Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("count"), Some(os("4").as_os_str())) ] }));

    // Short args
    test!(short:       [os("-l")]                  => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None) ] }));
    test!(short_then:  [os("-l"), os("4")]         => Ok(Matches { frees: vec![ os("4").as_os_str() ], flags: vec![ (Flag::Short(b'l'), None) ] }));
    test!(short_two:   [os("-lv")]                 => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'v'), None) ] }));
    test!(mixed:       [os("-v"), os("--long")]    => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'v'), None), (Flag::Long("long"), None) ] }));

    // Short args with values
    test!(bad_short:          [os("-l=equals")]       => Err(ParseError::ForbiddenValue { flag: Flag::Short(b'l') }));
    test!(short_none:         [os("-c")]              => Err(ParseError::NeedsValue     { flag: Flag::Short(b'c') }));
    test!(short_arg_eq:       [os("-c=4")]            => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'c'), Some(os("4").as_os_str())) ] }));
    test!(short_arg_then:     [os("-c"), os("4")]     => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'c'), Some(os("4").as_os_str())) ] }));
    test!(short_two_together: [os("-lctwo")]          => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }));
    test!(short_two_equals:   [os("-lc=two")]         => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }));
    test!(short_two_next:     [os("-lc"), os("two")]  => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }));
}
<commit_msg>Test for unknown arguments<commit_after>#![allow(unused_variables, dead_code)]

use std::ffi::{OsStr, OsString};

pub type ShortArg = u8;
pub type LongArg = &'static str;

#[derive(PartialEq, Debug)]
pub enum Flag {
    Short(ShortArg),
    Long(LongArg),
}

#[derive(PartialEq, Debug)]
pub enum Strictness {
    ComplainAboutRedundantArguments,
    UseLastArguments,
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum TakesValue {
    Necessary,
    Forbidden,
}

#[derive(PartialEq, Debug)]
pub struct Arg {
    short: Option<ShortArg>,
    long: LongArg,
    takes_value: TakesValue,
}

#[derive(PartialEq, Debug)]
pub struct Args(&'static [Arg]);

impl Args {
    fn lookup_short<'a>(&self, short: ShortArg) -> Result<&Arg, ParseError<'a>> {
        match self.0.into_iter().find(|arg| arg.short == Some(short)) {
            Some(arg)  => Ok(arg),
            None       => Err(ParseError::UnknownShortArgument { attempt: short })
        }
    }

    fn lookup_long<'a>(&self, long: &'a OsStr) -> Result<&Arg, ParseError<'a>> {
        match self.0.into_iter().find(|arg| arg.long == long) {
            Some(arg)  => Ok(arg),
            None       => Err(ParseError::UnknownArgument { attempt: long })
        }
    }
}

#[derive(PartialEq, Debug)]
pub struct Matches<'a> {
    /// Long and short arguments need to be kept in the same vector, because
    /// we usually want the one nearest the end to count.
    flags: Vec<(Flag, Option<&'a OsStr>)>,
    frees: Vec<&'a OsStr>,
}

#[derive(PartialEq, Debug)]
pub enum ParseError<'a> {
    NeedsValue { flag: Flag },
    ForbiddenValue { flag: Flag },
    UnknownShortArgument { attempt: ShortArg },
    UnknownArgument { attempt: &'a OsStr },
}

fn parse<'a>(args: Args, inputs: &'a [OsString]) -> Result<Matches<'a>, ParseError<'a>> {
    use std::os::unix::ffi::OsStrExt;
    use self::TakesValue::*;

    let mut parsing = true;

    let mut results = Matches {
        flags: Vec::new(),
        frees: Vec::new(),
    };

    let mut iter = inputs.iter();
    while let Some(arg) = iter.next() {
        let bytes = arg.as_bytes();

        if !parsing {
            results.frees.push(arg)
        }
        else if arg == "--" {
            parsing = false;
        }
        else if bytes.starts_with(b"--") {
            let long_arg_name = OsStr::from_bytes(&bytes[2..]);

            if let Some((before, after)) = split_on_equals(long_arg_name) {
                let arg = args.lookup_long(before)?;
                let flag = Flag::Long(arg.long);
                match arg.takes_value {
                    Necessary  => results.flags.push((flag, Some(after))),
                    Forbidden  => return Err(ParseError::ForbiddenValue { flag })
                }
            }
            else {
                let arg = args.lookup_long(long_arg_name)?;
                let flag = Flag::Long(arg.long);
                match arg.takes_value {
                    Forbidden  => results.flags.push((flag, None)),
                    Necessary  => {
                        if let Some(next_arg) = iter.next() {
                            results.flags.push((flag, Some(next_arg)));
                        }
                        else {
                            return Err(ParseError::NeedsValue { flag })
                        }
                    }
                }
            }
        }
        else if bytes.starts_with(b"-") && arg != "-" {
            let short_arg = OsStr::from_bytes(&bytes[1..]);
            if let Some((before, after)) = split_on_equals(short_arg) {
                let (arg_with_value, other_args) = before.as_bytes().split_last().unwrap();

                for byte in other_args {
                    let arg = args.lookup_short(*byte)?;
                    let flag = Flag::Short(*byte);
                    match arg.takes_value {
                        Forbidden  => results.flags.push((flag, None)),
                        Necessary  => return Err(ParseError::NeedsValue { flag })
                    }
                }

                let arg = args.lookup_short(*before.as_bytes().last().unwrap())?;
                let flag = Flag::Short(arg.short.unwrap());
                match arg.takes_value {
                    Necessary  => results.flags.push((flag, Some(after))),
                    Forbidden  => return Err(ParseError::ForbiddenValue { flag })
                }
            }
            else {
                for (index, byte) in bytes.into_iter().enumerate().skip(1) {
                    let arg = args.lookup_short(*byte)?;
                    let flag = Flag::Short(*byte);
                    match arg.takes_value {
                        Forbidden  => results.flags.push((flag, None)),
                        Necessary  => {
                            if index < bytes.len() - 1 {
                                let remnants = &bytes[index+1 ..];
                                results.flags.push((flag, Some(OsStr::from_bytes(remnants))));
                                break;
                            }
                            else if let Some(next_arg) = iter.next() {
                                results.flags.push((flag, Some(next_arg)));
                            }
                            else {
                                return Err(ParseError::NeedsValue { flag })
                            }
                        }
                    }
                }
            }
        }
        else {
            results.frees.push(arg)
        }
    }

    Ok(results)
}

/// Splits a string on its `=` character, returning the two substrings on
/// either side. Returns `None` if there’s no equals or a string is missing.
fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> {
    use std::os::unix::ffi::OsStrExt;

    if let Some(index) = input.as_bytes().iter().position(|elem| *elem == b'=') {
        let (before, after) = input.as_bytes().split_at(index);

        // The after string contains the = that we need to remove.
        if before.len() >= 1 && after.len() >= 2 {
            return Some((OsStr::from_bytes(before),
                         OsStr::from_bytes(&after[1..])))
        }
    }

    None
}

/// Creates an `OSString` (used in tests)
#[cfg(test)]
fn os(input: &'static str) -> OsString {
    let mut os = OsString::new();
    os.push(input);
    os
}

#[cfg(test)]
mod split_test {
    use super::{split_on_equals, os};

    macro_rules! test_split {
        ($name:ident: $input:expr => None) => {
            #[test]
            fn $name() {
                assert_eq!(split_on_equals(&os($input)),
                           None);
            }
        };

        ($name:ident: $input:expr => $before:expr, $after:expr) => {
            #[test]
            fn $name() {
                assert_eq!(split_on_equals(&os($input)),
                           Some((&*os($before), &*os($after))));
            }
        };
    }

    test_split!(empty:   ""   => None);
    test_split!(letter:  "a"  => None);

    test_split!(just:      "="    => None);
    test_split!(intro:     "=bbb" => None);
    test_split!(denou:  "aaa="    => None);
    test_split!(equals: "aaa=bbb" => "aaa", "bbb");

    test_split!(sort: "--sort=size"     => "--sort", "size");
    test_split!(more: "this=that=other" => "this",   "that=other");
}

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

    macro_rules! test {
        ($name:ident: $input:expr => $result:expr) => {
            #[test]
            fn $name() {
                let bits = $input;
                let results = parse(Args(TEST_ARGS), &bits);
                assert_eq!(results, $result);
            }
        };
    }

    static TEST_ARGS: &'static [Arg] = &[
        Arg { short: Some(b'l'), long: "long",     takes_value: TakesValue::Forbidden },
        Arg { short: Some(b'v'), long: "verbose",  takes_value: TakesValue::Forbidden },
        Arg { short: Some(b'c'), long: "count",    takes_value: TakesValue::Necessary }
    ];

    // Just filenames
    test!(empty:       []           => Ok(Matches { frees: vec![], flags: vec![] }));
    test!(one_arg:     [os("exa")]  => Ok(Matches { frees: vec![ os("exa").as_os_str() ], flags: vec![] }));

    // Dashes and double dashes
    test!(one_dash:    [os("-")]                 => Ok(Matches { frees: vec![ os("-").as_os_str() ],      flags: vec![] }));
    test!(two_dashes:  [os("--")]                => Ok(Matches { frees: vec![],                           flags: vec![] }));
    test!(two_file:    [os("--"), os("file")]    => Ok(Matches { frees: vec![ os("file").as_os_str() ],   flags: vec![] }));
    test!(two_arg_l:   [os("--"), os("--long")]  => Ok(Matches { frees: vec![ os("--long").as_os_str() ], flags: vec![] }));
    test!(two_arg_s:   [os("--"), os("-l")]      => Ok(Matches { frees: vec![ os("-l").as_os_str() ],     flags: vec![] }));

    // Long args
    test!(long:        [os("--long")]                  => Ok(Matches { frees: vec![],                      flags: vec![ (Flag::Long("long"), None) ] }));
    test!(long_then:   [os("--long"), os("4")]         => Ok(Matches { frees: vec![ os("4").as_os_str() ], flags: vec![ (Flag::Long("long"), None) ] }));
    test!(long_two:    [os("--long"), os("--verbose")] => Ok(Matches { frees: vec![],                      flags: vec![ (Flag::Long("long"), None), (Flag::Long("verbose"), None) ] }));

    // Long args with values
    test!(bad_equals:  [os("--long=equals")]    => Err(ParseError::ForbiddenValue { flag: Flag::Long("long") }));
    test!(no_arg:      [os("--count")]          => Err(ParseError::NeedsValue     { flag: Flag::Long("count") }));
    test!(arg_equals:  [os("--count=4")]        => Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("count"), Some(os("4").as_os_str())) ] }));
    test!(arg_then:    [os("--count"), os("4")] => Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("count"), Some(os("4").as_os_str())) ] }));

    // Short args
    test!(short:       [os("-l")]                  => Ok(Matches { frees: vec![],                      flags: vec![ (Flag::Short(b'l'), None) ] }));
    test!(short_then:  [os("-l"), os("4")]         => Ok(Matches { frees: vec![ os("4").as_os_str() ], flags: vec![ (Flag::Short(b'l'), None) ] }));
    test!(short_two:   [os("-lv")]                 => Ok(Matches { frees: vec![],                      flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'v'), None) ] }));
    test!(mixed:       [os("-v"), os("--long")]    => Ok(Matches { frees: vec![],                      flags: vec![ (Flag::Short(b'v'), None), (Flag::Long("long"), None) ] }));

    // Short args with values
    test!(bad_short:          [os("-l=equals")]       => Err(ParseError::ForbiddenValue { flag: Flag::Short(b'l') }));
    test!(short_none:         [os("-c")]              => Err(ParseError::NeedsValue     { flag: Flag::Short(b'c') }));
    test!(short_arg_eq:       [os("-c=4")]            => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'c'), Some(os("4").as_os_str())) ] }));
    test!(short_arg_then:     [os("-c"), os("4")]     => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'c'), Some(os("4").as_os_str())) ] }));
    test!(short_two_together: [os("-lctwo")]          => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }));
    test!(short_two_equals:   [os("-lc=two")]         => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }));
    test!(short_two_next:     [os("-lc"), os("two")]  => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }));

    // Unknown args
    test!(unknown_long:          [os("--quiet")]      => Err(ParseError::UnknownArgument      { attempt: os("quiet").as_os_str() }));
    test!(unknown_long_eq:       [os("--quiet=shhh")] => Err(ParseError::UnknownArgument      { attempt: os("quiet").as_os_str() }));
    test!(unknown_short:         [os("-q")]           => Err(ParseError::UnknownShortArgument { attempt: b'q' }));
    test!(unknown_short_2nd:     [os("-lq")]          => Err(ParseError::UnknownShortArgument { attempt: b'q' }));
    test!(unknown_short_eq:      [os("-q=shhh")]      => Err(ParseError::UnknownShortArgument { attempt: b'q' }));
    test!(unknown_short_2nd_eq:  [os("-lq=shhh")]     => Err(ParseError::UnknownShortArgument { attempt: b'q' }));
}
<|endoftext|>


<commit_before>//! High level wrapper for libxml2's SAX parser

use std::cast::transmute;
use std::comm::Chan;
use std::hashmap::*;
use std::libc::{c_int, c_void};
use std::ptr::null;
use std::str::raw::{from_buf, from_buf_len};

use self::error::ErrorData;

pub mod error;
pub mod ffi;

#[deriving(Eq, Clone)]
pub enum Msg {
    StartElement(~str, Attributes),
    EndElement(~str),
    Characters(~str),
}

impl ToStr for Msg {
    fn to_str(&self) -> ~str {
        match *self {
            StartElement(ref name, ref atts) => fmt!("<%s%s>", *name, atts.to_str()),
            EndElement(ref name) => fmt!("</%s>", *name),
            Characters(ref chrs) => chrs.clone(),
        }
    }
}

#[deriving(Eq, Clone)]
pub struct Attributes(HashMap<~str, ~str>);

impl ToStr for Attributes {
    fn to_str(&self) -> ~str {
        do self.iter().transform |(k, v)| {
            fmt!(" %s=\"%s\"", *k, *v)
        }.to_owned_vec().concat()
    }
}

pub type ParseResult = Result<Msg, ErrorData>;

pub fn parse(src: &str, chan: Chan<ParseResult>) {
    unsafe {
        do src.as_c_str |c_str| {
            ffi::xmlSAXUserParseMemory(&new_sax_handler(),
                                       transmute(&chan),
                                       c_str, src.len() as c_int);
            ffi::xmlCleanupParser();
        }
    }
}

unsafe fn chan_from_ptr(ctx: *c_void) -> &Chan<ParseResult> {
    transmute(ctx)
}

extern "C" fn start_element(ctx: *c_void, name: *ffi::xmlChar, atts: **ffi::xmlChar) {
    unsafe {
        let mut map = Attributes(HashMap::new());
        let mut ptr = atts;
        while !ptr.is_null() && !(*ptr).is_null() {
            map.insert(from_buf(*ptr),
                       from_buf(*(ptr + 1)));
            ptr = ptr + 2;
        }

        chan_from_ptr(ctx).send(
            Ok(StartElement(from_buf(name), map))
        );
    }
}

extern "C" fn end_element(ctx: *c_void, name: *ffi::xmlChar) {
    unsafe {
        chan_from_ptr(ctx).send(
            Ok(EndElement(from_buf(name)))
        );
    }
}

extern "C" fn characters(ctx: *c_void, ch: *ffi::xmlChar, len: c_int) {
    unsafe {
        chan_from_ptr(ctx).send(
            Ok(Characters(from_buf_len(ch, len as uint)))
        );
    }
}

extern "C" fn serror(ctx: *c_void, error: *ffi::xmlError) {
    unsafe {
        do ErrorData::from_ptr(error).map |err| {
            chan_from_ptr(ctx).send(Err(err.clone()));
        };
    }
}

fn new_sax_handler() -> ffi::xmlSAXHandler {
    ffi::xmlSAXHandler {
        internalSubset:         null(),
        isStandalone:           null(),
        hasInternalSubset:      null(),
        hasExternalSubset:      null(),
        resolveEntity:          null(),
        getEntity:              null(),
        entityDecl:             null(),
        notationDecl:           null(),
        attributeDecl:          null(),
        elementDecl:            null(),
        unparsedEntityDecl:     null(),
        setDocumentLocator:     null(),
        startDocument:          null(),
        endDocument:            null(),
        startElement:           start_element,
        endElement:             end_element,
        reference:              null(),
        characters:             characters,
        ignorableWhitespace:    null(),
        processingInstruction:  null(),
        comment:                null(),
        warning:                null(),
        error:                  null(),
        fatalError:             null(),
        getParameterEntity:     null(),
        cdataBlock:             null(),
        externalSubset:         null(),
        initialized:            ffi::XML_SAX2_MAGIC,
        _private:               null(),
        startElementNs:         null(),
        endElementNs:           null(),
        serror:                 serror,
    }
}
<commit_msg>Fix build for changes to std<commit_after>//! High level wrapper for libxml2's SAX parser

use std::cast::transmute;
use std::comm::Chan;
use std::hashmap::*;
use std::libc::{c_char, c_int, c_void};
use std::ptr::null;
use std::str::raw::{from_c_str, from_buf_len};

use self::error::ErrorData;

pub mod error;
pub mod ffi;

#[deriving(Eq, Clone)]
pub enum Msg {
    StartElement(~str, Attributes),
    EndElement(~str),
    Characters(~str),
}

impl ToStr for Msg {
    fn to_str(&self) -> ~str {
        match *self {
            StartElement(ref name, ref atts) => fmt!("<%s%s>", *name, atts.to_str()),
            EndElement(ref name) => fmt!("</%s>", *name),
            Characters(ref chrs) => chrs.clone(),
        }
    }
}

#[deriving(Eq, Clone)]
pub struct Attributes(HashMap<~str, ~str>);

impl ToStr for Attributes {
    fn to_str(&self) -> ~str {
        do self.iter().map |(k, v)| {
            fmt!(" %s=\"%s\"", *k, *v)
        }.to_owned_vec().concat()
    }
}

pub type ParseResult = Result<Msg, ErrorData>;

pub fn parse(src: &str, chan: Chan<ParseResult>) {
    unsafe {
        do src.to_c_str().with_ref |c_str| {
            ffi::xmlSAXUserParseMemory(&new_sax_handler(),
                                       transmute(&chan),
                                       c_str, src.len() as c_int);
            ffi::xmlCleanupParser();
        }
    }
}

unsafe fn chan_from_ptr(ctx: *c_void) -> &Chan<ParseResult> {
    transmute(ctx)
}

extern "C" fn start_element(ctx: *c_void, name: *ffi::xmlChar, atts: **ffi::xmlChar) {
    unsafe {
        let mut map = Attributes(HashMap::new());
        let mut ptr = atts as **c_char;
        while !ptr.is_null() && !(*ptr).is_null() {
            map.insert(from_c_str(*ptr),
                       from_c_str(*(ptr + 1)));
            ptr = ptr + 2;
        }

        chan_from_ptr(ctx).send(
            Ok(StartElement(from_c_str(name as *c_char), map))
        );
    }
}

extern "C" fn end_element(ctx: *c_void, name: *ffi::xmlChar) {
    unsafe {
        chan_from_ptr(ctx).send(
            Ok(EndElement(from_c_str(name as *c_char)))
        );
    }
}

extern "C" fn characters(ctx: *c_void, ch: *ffi::xmlChar, len: c_int) {
    unsafe {
        chan_from_ptr(ctx).send(
            Ok(Characters(from_buf_len(ch, len as uint)))
        );
    }
}

extern "C" fn serror(ctx: *c_void, error: *ffi::xmlError) {
    unsafe {
        do ErrorData::from_ptr(error).map |err| {
            chan_from_ptr(ctx).send(Err(err.clone()));
        };
    }
}

fn new_sax_handler() -> ffi::xmlSAXHandler {
    ffi::xmlSAXHandler {
        internalSubset:         null(),
        isStandalone:           null(),
        hasInternalSubset:      null(),
        hasExternalSubset:      null(),
        resolveEntity:          null(),
        getEntity:              null(),
        entityDecl:             null(),
        notationDecl:           null(),
        attributeDecl:          null(),
        elementDecl:            null(),
        unparsedEntityDecl:     null(),
        setDocumentLocator:     null(),
        startDocument:          null(),
        endDocument:            null(),
        startElement:           start_element,
        endElement:             end_element,
        reference:              null(),
        characters:             characters,
        ignorableWhitespace:    null(),
        processingInstruction:  null(),
        comment:                null(),
        warning:                null(),
        error:                  null(),
        fatalError:             null(),
        getParameterEntity:     null(),
        cdataBlock:             null(),
        externalSubset:         null(),
        initialized:            ffi::XML_SAX2_MAGIC,
        _private:               null(),
        startElementNs:         null(),
        endElementNs:           null(),
        serror:                 serror,
    }
}
<|endoftext|>


<commit_before>#![feature(plugin, custom_attribute)]
#![plugin(gfx_macros)]

extern crate piston;
extern crate piston_window;
extern crate vecmath;
extern crate camera_controllers;
extern crate gfx;
extern crate gfx_device_gl;
extern crate glfw_window;

use std::cell::RefCell;
use std::rc::Rc;
use piston_window::*;
use piston::event::*;
use piston::window::{ AdvancedWindow, WindowSettings };
use camera_controllers::{
    FirstPersonSettings,
    FirstPerson,
    CameraPerspective,
    model_view_projection
};
use gfx::traits::*;
use glfw_window::{ GlfwWindow, OpenGL };

//----------------------------------------
// Cube associated data

#[vertex_format]
#[derive(Copy, Clone)]
struct Vertex {
    #[as_float]
    a_pos: [i8; 3],
    #[as_float]
    a_tex_coord: [u8; 2],
}

impl Vertex {
    fn new(pos: [i8; 3], tc: [u8; 2]) -> Vertex {
        Vertex {
            a_pos: pos,
            a_tex_coord: tc,
        }
    }
}

#[shader_param]
struct Params<R: gfx::Resources> {
    u_model_view_proj: [[f32; 4]; 4],
    t_color: gfx::shade::TextureParam<R>,
}

//----------------------------------------

fn main() {
    let (win_width, win_height) = (640, 480);
    let window = Rc::new(RefCell::new(GlfwWindow::new(
        OpenGL::_3_2,
        WindowSettings::new("piston-example-gfx_cube", [win_width, win_height])
        .exit_on_esc(true)
        .samples(4)
    ).capture_cursor(true)));

    let events = PistonWindow::new(window, empty_app());

    let vertex_data = vec![
        //top (0, 0, 1)
        Vertex::new([-1, -1,  1], [0, 0]),
        Vertex::new([ 1, -1,  1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //bottom (0, 0, -1)
        Vertex::new([ 1,  1, -1], [0, 0]),
        Vertex::new([-1,  1, -1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
        //right (1, 0, 0)
        Vertex::new([ 1, -1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([ 1, -1,  1], [0, 1]),
        //left (-1, 0, 0)
        Vertex::new([-1,  1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([-1,  1, -1], [0, 1]),
        //front (0, 1, 0)
        Vertex::new([-1,  1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //back (0, -1, 0)
        Vertex::new([ 1, -1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
    ];

    let mesh = events.canvas.borrow_mut().factory.create_mesh(&vertex_data);

    let index_data: &[u8] = &[
         0,  1,  2,  2,  3,  0, // top
         4,  6,  5,  6,  4,  7, // bottom
         8,  9, 10, 10, 11,  8, // right
        12, 14, 13, 14, 12, 16, // left
        16, 18, 17, 18, 16, 19, // front
        20, 21, 22, 22, 23, 20, // back
    ];

    let slice = events.canvas.borrow_mut().factory.create_buffer_index(index_data)
                       .to_slice(gfx::PrimitiveType::TriangleList);

    let tinfo = gfx::tex::TextureInfo {
        width: 1, height: 1, depth: 1, levels: 1,
        kind: gfx::tex::TextureKind::Texture2D,
        format: gfx::tex::RGBA8,
    };
    let img_info = tinfo.to_image_info();
    let texture = events.canvas.borrow_mut().factory.create_texture(tinfo).unwrap();
    events.canvas.borrow_mut().factory.update_texture(
        &texture,
        &img_info,
        &[0x20u8, 0xA0, 0xC0, 0x00],
        Some(gfx::tex::TextureKind::Texture2D)
    ).unwrap();

    let sampler = events.canvas.borrow_mut().factory.create_sampler(
        gfx::tex::SamplerInfo::new(gfx::tex::FilterMethod::Bilinear,
            gfx::tex::WrapMode::Clamp));

    let program = {
        let canvas = &mut *events.canvas.borrow_mut();
        let vertex = gfx::ShaderSource {
            glsl_120: Some(include_bytes!("cube_120.glslv")),
            glsl_150: Some(include_bytes!("cube_150.glslv")),
            .. gfx::ShaderSource::empty()
        };
        let fragment = gfx::ShaderSource {
            glsl_120: Some(include_bytes!("cube_120.glslf")),
            glsl_150: Some(include_bytes!("cube_150.glslf")),
            .. gfx::ShaderSource::empty()
        };
        canvas.factory.link_program_source(vertex, fragment,
            &canvas.device.get_capabilities()).unwrap()
    };

    let mut data = Params {
        u_model_view_proj: vecmath::mat4_id(),
        t_color: (texture, Some(sampler)),
    };

    let model = vecmath::mat4_id();
    let projection = CameraPerspective {
        fov: 90.0, near_clip: 0.1, far_clip: 1000.0,
        aspect_ratio: (win_width as f32) / (win_height as f32)
    }.projection();
    let mut first_person = FirstPerson::new([0.5, 0.5, 4.0],
        FirstPersonSettings::keyboard_wasd());
    let state = gfx::DrawState::new().depth(gfx::state::Comparison::LessEqual, true);

    for e in events {
        first_person.event(&e);

        e.draw_3d(|canvas| {
            let args = e.render_args().unwrap();
            canvas.renderer.clear(
                gfx::ClearData {
                    color: [0.3, 0.3, 0.3, 1.0],
                    depth: 1.0,
                    stencil: 0,
                },
                gfx::COLOR | gfx::DEPTH,
                &canvas.output
            );
            data.u_model_view_proj = model_view_projection(
                model,
                first_person.camera(args.ext_dt).orthogonal(),
                projection
            );
            canvas.renderer.draw(&(&mesh, slice.clone(), &program, &data, &state),
                &canvas.output).unwrap();
        });
    }
}
<commit_msg>Use draw size for projection<commit_after>#![feature(plugin, custom_attribute)]
#![plugin(gfx_macros)]

extern crate piston;
extern crate piston_window;
extern crate vecmath;
extern crate camera_controllers;
extern crate gfx;
extern crate gfx_device_gl;
extern crate glfw_window;

use std::cell::RefCell;
use std::rc::Rc;
use piston_window::*;
use piston::event::*;
use piston::window::{ AdvancedWindow, WindowSettings };
use camera_controllers::{
    FirstPersonSettings,
    FirstPerson,
    CameraPerspective,
    model_view_projection
};
use gfx::traits::*;
use glfw_window::{ GlfwWindow, OpenGL };

//----------------------------------------
// Cube associated data

#[vertex_format]
#[derive(Copy, Clone)]
struct Vertex {
    #[as_float]
    a_pos: [i8; 3],
    #[as_float]
    a_tex_coord: [u8; 2],
}

impl Vertex {
    fn new(pos: [i8; 3], tc: [u8; 2]) -> Vertex {
        Vertex {
            a_pos: pos,
            a_tex_coord: tc,
        }
    }
}

#[shader_param]
struct Params<R: gfx::Resources> {
    u_model_view_proj: [[f32; 4]; 4],
    t_color: gfx::shade::TextureParam<R>,
}

//----------------------------------------

fn main() {
    let (win_width, win_height) = (640, 480);
    let window = Rc::new(RefCell::new(GlfwWindow::new(
        OpenGL::_3_2,
        WindowSettings::new("piston-example-gfx_cube", [win_width, win_height])
        .exit_on_esc(true)
        .samples(4)
    ).capture_cursor(true)));

    let events = PistonWindow::new(window, empty_app());

    let vertex_data = vec![
        //top (0, 0, 1)
        Vertex::new([-1, -1,  1], [0, 0]),
        Vertex::new([ 1, -1,  1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //bottom (0, 0, -1)
        Vertex::new([ 1,  1, -1], [0, 0]),
        Vertex::new([-1,  1, -1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
        //right (1, 0, 0)
        Vertex::new([ 1, -1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([ 1, -1,  1], [0, 1]),
        //left (-1, 0, 0)
        Vertex::new([-1,  1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([-1,  1, -1], [0, 1]),
        //front (0, 1, 0)
        Vertex::new([-1,  1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //back (0, -1, 0)
        Vertex::new([ 1, -1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
    ];

    let mesh = events.canvas.borrow_mut().factory.create_mesh(&vertex_data);

    let index_data: &[u8] = &[
         0,  1,  2,  2,  3,  0, // top
         4,  6,  5,  6,  4,  7, // bottom
         8,  9, 10, 10, 11,  8, // right
        12, 14, 13, 14, 12, 16, // left
        16, 18, 17, 18, 16, 19, // front
        20, 21, 22, 22, 23, 20, // back
    ];

    let slice = events.canvas.borrow_mut().factory.create_buffer_index(index_data)
                       .to_slice(gfx::PrimitiveType::TriangleList);

    let tinfo = gfx::tex::TextureInfo {
        width: 1, height: 1, depth: 1, levels: 1,
        kind: gfx::tex::TextureKind::Texture2D,
        format: gfx::tex::RGBA8,
    };
    let img_info = tinfo.to_image_info();
    let texture = events.canvas.borrow_mut().factory.create_texture(tinfo).unwrap();
    events.canvas.borrow_mut().factory.update_texture(
        &texture,
        &img_info,
        &[0x20u8, 0xA0, 0xC0, 0x00],
        Some(gfx::tex::TextureKind::Texture2D)
    ).unwrap();

    let sampler = events.canvas.borrow_mut().factory.create_sampler(
        gfx::tex::SamplerInfo::new(gfx::tex::FilterMethod::Bilinear,
            gfx::tex::WrapMode::Clamp));

    let program = {
        let canvas = &mut *events.canvas.borrow_mut();
        let vertex = gfx::ShaderSource {
            glsl_120: Some(include_bytes!("cube_120.glslv")),
            glsl_150: Some(include_bytes!("cube_150.glslv")),
            .. gfx::ShaderSource::empty()
        };
        let fragment = gfx::ShaderSource {
            glsl_120: Some(include_bytes!("cube_120.glslf")),
            glsl_150: Some(include_bytes!("cube_150.glslf")),
            .. gfx::ShaderSource::empty()
        };
        canvas.factory.link_program_source(vertex, fragment,
            &canvas.device.get_capabilities()).unwrap()
    };

    let mut data = Params {
        u_model_view_proj: vecmath::mat4_id(),
        t_color: (texture, Some(sampler)),
    };

    let get_projection = |w: &PistonWindow<GlfwWindow>| {
        use piston::window::Window;

        let draw_size = w.window.borrow().draw_size();
        CameraPerspective {
            fov: 90.0, near_clip: 0.1, far_clip: 1000.0,
            aspect_ratio: (draw_size.width as f32) / (draw_size.height as f32)
        }.projection()
    };

    let model = vecmath::mat4_id();
    let mut projection = get_projection(&events);
    let mut first_person = FirstPerson::new([0.5, 0.5, 4.0],
        FirstPersonSettings::keyboard_wasd());
    let state = gfx::DrawState::new().depth(gfx::state::Comparison::LessEqual, true);

    for e in events {
        first_person.event(&e);

        e.draw_3d(|canvas| {
            let args = e.render_args().unwrap();
            canvas.renderer.clear(
                gfx::ClearData {
                    color: [0.3, 0.3, 0.3, 1.0],
                    depth: 1.0,
                    stencil: 0,
                },
                gfx::COLOR | gfx::DEPTH,
                &canvas.output
            );
            data.u_model_view_proj = model_view_projection(
                model,
                first_person.camera(args.ext_dt).orthogonal(),
                projection
            );
            canvas.renderer.draw(&(&mesh, slice.clone(), &program, &data, &state),
                &canvas.output).unwrap();
        });

        if let Some(_) = e.resize_args() {
            projection = get_projection(&e);
        }
    }
}
<|endoftext|>


<commit_before>use common::{R, sq};
use monomial::Monomial;
use mesh::{Mesh, FENum};
use wg_solution::WGSolution;

use std::num::sqrt;

pub fn err_L2_norm<Mon:Monomial,MeshT:Mesh<Mon>>(exact_sol: &fn(&[R])->R, approx: &WGSolution<Mon,MeshT>) -> R {
  let mesh = approx.basis().mesh();
  let sum_fe_sq_err_intgs = range(0, mesh.num_fes()).fold(0 as R, |sum_fe_sq_err_intgs, fe| {
    let sq_err_at_pt = |x: &[R], x_int_rel: &[R]| {
      sq(exact_sol(x) - approx.value_at_int_rel(FENum(fe), x_int_rel))
    };
    sum_fe_sq_err_intgs + mesh.intg_mixed_global_and_facerel_fn_on_fe_int(sq_err_at_pt, FENum(fe))
  });

  sqrt(sum_fe_sq_err_intgs)
}

/*
# Solution evaluation functions

# Return an interior-relative polynomial vector which represents the weak gradient of the passed
# solution on the indicated finite element interior.
function wg_sol_wgrad_on_interior(fe::FENum, wg_sol::WGSolution)
  const basis = wg_sol.basis
  const mesh = basis.mesh
  const wgrad_solver = basis.wgrad_solver
  const sol_coefs = wg_sol.basis_coefs
  const boundary_projs = wg_sol.boundary_projs
  const fe_oshape = Mesh.oriented_shape_for_fe(fe, mesh)

  # The weak gradient of the solution is the sum of the weak gradients of the functions which equal
  # the solution on one face of our finite element and are zero elsewhere.
  total_wgrad = Poly.zero_poly_vec(Mesh.space_dim(mesh))

  # Add contributions for the terms of the solution's interior polynomial.
  for int_monn=monnum(1):basis.mons_per_fe_interior
    const beln = WGBasis.interior_mon_bel_num(fe, int_monn, basis)
    total_wgrad += sol_coefs[beln] * WGBasis.wgrad_interior_mon(int_monn, fe_oshape, basis)
  end

  # Add contributions for the solution's side polynomials, for both non-boundary and boundary sides.
  for sf=feface_one:Mesh.num_side_faces_for_shape(fe_oshape, mesh)
    if Mesh.is_boundary_side(fe, sf, mesh)
      const proj_coefs = boundary_projs[(fe, sf)]
      for side_monn=monnum(1):basis.mons_per_fe_side
        total_wgrad += proj_coefs[side_monn] * WGBasis.wgrad_side_mon(side_monn, fe_oshape, sf, basis)
      end
    else
      for side_monn=monnum(1):basis.mons_per_fe_side
        const beln = WGBasis.side_mon_bel_num(fe, sf, side_monn, basis)
        total_wgrad += sol_coefs[beln] * WGBasis.wgrad_side_mon(side_monn, fe_oshape, sf, basis)
      end
    end
  end

  total_wgrad
end

# Error Estimates

function err_H1_norm(exact_sol::Function,
                     grad_exact_sol::Array{Function,1},
                     wg_sol::WGSolution)
  const mesh = wg_sol.basis.mesh
  const d = Mesh.space_dim(mesh)

  # Work data for sum_sq_errs function below.
  const fe_rel_x = Array(R, d)
  const fe_origin = Array(R, d)

  integral_sq_errs = zeroR

  for fe=fenum(1):Mesh.num_fes(mesh)
    Mesh.fe_interior_origin!(fe, fe_origin, mesh)

    const approx = wg_sol_interior_poly(fe, wg_sol)
    const grad_approx = Poly.grad(approx)

    function sum_sq_errs(x::Vector{R})
      # set the fe-relative point coordinates
      for i=1:d
        fe_rel_x[i] = x[i] - fe_origin[i]
      end
      # compute the sum of the square first partial derivative errors
      sum_first_partial_sq_errs = zeroR
      for n=dim(1):d
        const partial_diff = grad_exact_sol[n](x) - Poly.value_at(grad_approx[n], fe_rel_x)
        sum_first_partial_sq_errs += partial_diff * partial_diff
      end
      const err = exact_sol(x) - Poly.value_at(approx, fe_rel_x)
      err * err + sum_first_partial_sq_errs
    end

    integral_sq_errs += Mesh.integral_global_x_face_rel_on_fe_face(sum_sq_errs,
                                                                   Mesh.one_mon(mesh),
                                                                   fe, Mesh.interior_face,
                                                                   mesh)
  end

  sqrt(integral_sq_errs)
end

function err_vs_proj_L2_norm(exact_sol::Function, wg_sol::WGSolution)
  const basis = wg_sol.basis
  const mesh = basis.mesh

  integral_sq_err = zeroR
  for fe=fenum(1):Mesh.num_fes(mesh)
    const proj_poly = Proj.project_onto_fe_face_supported_approx_subspace_as_poly(exact_sol, fe, Mesh.interior_face, basis)
    const wg_sol_poly = wg_sol_interior_poly(fe, wg_sol)
    const err = proj_poly - wg_sol_poly
    const sq_err = Poly.canonical_form(err * err)
    const fe_oshape = Mesh.oriented_shape_for_fe(fe, mesh)
    integral_sq_err += Mesh.integral_face_rel_on_oshape_face(sq_err,
                                                             fe_oshape, Mesh.interior_face,
                                                             mesh)
  end

  sqrt(integral_sq_err)
end

function err_grad_vs_wgrad_L2_norm(exact_sol_grad::Function, wg_sol::WGSolution)
  const basis = wg_sol.basis
  const mesh = basis.mesh
  const one = Mesh.one_mon(mesh)
  const d = Mesh.space_dim(mesh)
  const fe_rel_x = Array(R, d)
  const fe_origin = Array(R, d)

  integral_sq_err = zeroR
  for fe=fenum(1):Mesh.num_fes(mesh)
    Mesh.fe_interior_origin!(fe, fe_origin, mesh)
    const wgrad = wg_sol_wgrad_on_interior(fe, wg_sol)
    function sq_err(x::Vector{R})
      for i=1:d
        fe_rel_x[i] = x[i] - fe_origin[i]
      end
      const diff = exact_sol_grad(x) - Poly.value_at(wgrad, fe_rel_x)
      dot(diff, diff)
    end
    integral_sq_err += Mesh.integral_global_x_face_rel_on_fe_face(sq_err,
                                                                  one,
                                                                  fe, Mesh.interior_face,
                                                                  mesh)
  end
  sqrt(integral_sq_err)
end

# Compute semi-norm ||| Q_h u - u_h |||_vbf = sqrt(vbf(Q u - u_h, Q u - u_h)),
# where Q_h is piecwise L2 projection onto finite element faces.
function err_vs_proj_vbf_seminorm(exact_sol::Function, wg_sol::WGSolution, vbf::AbstractVariationalBilinearForm)
  const basis = wg_sol.basis
  const mesh = basis.mesh
  const vbf_symm = VBF.is_symmetric(vbf)

  # work arrays
  const side_diffs = Array(Vector{R}, Mesh.max_num_shape_sides(basis.mesh))
  const is_nb_side = Array(Bool, Mesh.max_num_shape_sides(basis.mesh))

  # We must evaluate
  #   sum_{fe} vbf(diff(fe), diff(fe)),
  # where
  #   diff(fe) = diff_int(fe) + sum_{s in nb-sides(fe)} diff_s,
  # and diff_f for a face f is the L2 projection onto f of the exact solution minus the wg
  # solution on the face, and 0 elsewhere.
  # By the bilinearity of the vbf, we need to sum the contributions from each finite element
  # of the vbf applied to the differences on each interior or non-boundary side against that
  # on each interior or non-boundary side.

  sum = zeroR
  for fe=fenum(1):Mesh.num_fes(mesh)
    const fe_oshape = Mesh.oriented_shape_for_fe(fe, mesh)
    const num_sides = Mesh.num_side_faces_for_shape(fe_oshape, mesh)
    # Find non-boundary sides
    for sf=feface_one:num_sides
      is_nb_side[sf] = !Mesh.is_boundary_side(fe, sf, mesh)
    end

    # Compute exact solution projection minus wg solution for all faces.
    const int_diff = Proj.project_onto_fe_face_supported_approx_subspace(exact_sol, fe, Mesh.interior_face, basis) -
                     wg_sol_interior_coefs(fe, wg_sol)
    for sf=feface_one:num_sides if is_nb_side[sf]
      diff = Proj.project_onto_fe_face_supported_approx_subspace(exact_sol, fe, sf, basis)
      diff -= wg_sol_side_coefs(fe, sf, wg_sol)
      side_diffs[sf] = diff
    end end

    # interior vs interior
    sum += VBF.poly_on_face_vs_poly_on_face(fe_oshape,
                                            int_diff, Mesh.interior_face,
                                            int_diff, Mesh.interior_face,
                                            basis,
                                            vbf)

    for sf=feface_one:num_sides if is_nb_side[sf]
      const sf_diff = side_diffs[sf]

      # Add contributions from pairings of a side and interior.

      # interior vs side
      const int_vs_side = VBF.poly_on_face_vs_poly_on_face(fe_oshape,
                                                           int_diff, Mesh.interior_face,
                                                           sf_diff, sf,
                                                           basis,
                                                           vbf)
      sum += int_vs_side

      # side vs interior
      sum += if vbf_symm  int_vs_side
             else VBF.poly_on_face_vs_poly_on_face(fe_oshape,
                                                   sf_diff, sf,
                                                   int_diff, Mesh.interior_face,
                                                   basis,
                                                   vbf) end

      # Add contributions for pairings of sides.

      # side vs side
      if vbf_symm
        # contributions from pairings of unequal sides (symmetric case)
        for sf2=fefacenum(sf+1):num_sides if is_nb_side[sf2]
          sum += 2 * VBF.poly_on_face_vs_poly_on_face(fe_oshape, sf_diff, sf, side_diffs[sf2], sf2, basis, vbf)
        end end
        # contributions from side self-pairings (symmetric case)
        sum += VBF.poly_on_face_vs_poly_on_face(fe_oshape, sf_diff, sf, sf_diff, sf, basis, vbf)
      else # vbf not symmetric
        # contributions for all side vs side pairings (asymmetric case)
        for sf2=feface_one:num_sides if is_nb_side[sf2]
          sum += VBF.poly_on_face_vs_poly_on_face(fe_oshape, sf_diff, sf, side_diffs[sf2], sf2, basis, vbf)
        end end
      end
    end end # non-boundary sides
  end
  sqrt(sum)
end

end
*/
<commit_msg>Minor clean in err_L2_norm.<commit_after>use common::{R, sq};
use monomial::Monomial;
use mesh::{Mesh, FENum};
use wg_solution::WGSolution;

use std::num::sqrt;

pub fn err_L2_norm<Mon:Monomial,MeshT:Mesh<Mon>>(exact_sol: &fn(&[R])->R, approx: &WGSolution<Mon,MeshT>) -> R {
  let mesh = approx.basis().mesh();
  let sum_fe_sq_err_intgs = range(0, mesh.num_fes()).fold(0 as R, |sum_fe_sq_err_intgs, fe| {
    let fe = FENum(fe);
    let sq_err_at_pt = |x: &[R], x_int_rel: &[R]| {
      sq(exact_sol(x) - approx.value_at_int_rel(fe, x_int_rel))
    };
    sum_fe_sq_err_intgs + mesh.intg_mixed_global_and_facerel_fn_on_fe_int(sq_err_at_pt, fe)
  });

  sqrt(sum_fe_sq_err_intgs)
}

/*
# Solution evaluation functions

# Return an interior-relative polynomial vector which represents the weak gradient of the passed
# solution on the indicated finite element interior.
function wg_sol_wgrad_on_interior(fe::FENum, wg_sol::WGSolution)
  const basis = wg_sol.basis
  const mesh = basis.mesh
  const wgrad_solver = basis.wgrad_solver
  const sol_coefs = wg_sol.basis_coefs
  const boundary_projs = wg_sol.boundary_projs
  const fe_oshape = Mesh.oriented_shape_for_fe(fe, mesh)

  # The weak gradient of the solution is the sum of the weak gradients of the functions which equal
  # the solution on one face of our finite element and are zero elsewhere.
  total_wgrad = Poly.zero_poly_vec(Mesh.space_dim(mesh))

  # Add contributions for the terms of the solution's interior polynomial.
  for int_monn=monnum(1):basis.mons_per_fe_interior
    const beln = WGBasis.interior_mon_bel_num(fe, int_monn, basis)
    total_wgrad += sol_coefs[beln] * WGBasis.wgrad_interior_mon(int_monn, fe_oshape, basis)
  end

  # Add contributions for the solution's side polynomials, for both non-boundary and boundary sides.
  for sf=feface_one:Mesh.num_side_faces_for_shape(fe_oshape, mesh)
    if Mesh.is_boundary_side(fe, sf, mesh)
      const proj_coefs = boundary_projs[(fe, sf)]
      for side_monn=monnum(1):basis.mons_per_fe_side
        total_wgrad += proj_coefs[side_monn] * WGBasis.wgrad_side_mon(side_monn, fe_oshape, sf, basis)
      end
    else
      for side_monn=monnum(1):basis.mons_per_fe_side
        const beln = WGBasis.side_mon_bel_num(fe, sf, side_monn, basis)
        total_wgrad += sol_coefs[beln] * WGBasis.wgrad_side_mon(side_monn, fe_oshape, sf, basis)
      end
    end
  end

  total_wgrad
end

# Error Estimates

function err_H1_norm(exact_sol::Function,
                     grad_exact_sol::Array{Function,1},
                     wg_sol::WGSolution)
  const mesh = wg_sol.basis.mesh
  const d = Mesh.space_dim(mesh)

  # Work data for sum_sq_errs function below.
  const fe_rel_x = Array(R, d)
  const fe_origin = Array(R, d)

  integral_sq_errs = zeroR

  for fe=fenum(1):Mesh.num_fes(mesh)
    Mesh.fe_interior_origin!(fe, fe_origin, mesh)

    const approx = wg_sol_interior_poly(fe, wg_sol)
    const grad_approx = Poly.grad(approx)

    function sum_sq_errs(x::Vector{R})
      # set the fe-relative point coordinates
      for i=1:d
        fe_rel_x[i] = x[i] - fe_origin[i]
      end
      # compute the sum of the square first partial derivative errors
      sum_first_partial_sq_errs = zeroR
      for n=dim(1):d
        const partial_diff = grad_exact_sol[n](x) - Poly.value_at(grad_approx[n], fe_rel_x)
        sum_first_partial_sq_errs += partial_diff * partial_diff
      end
      const err = exact_sol(x) - Poly.value_at(approx, fe_rel_x)
      err * err + sum_first_partial_sq_errs
    end

    integral_sq_errs += Mesh.integral_global_x_face_rel_on_fe_face(sum_sq_errs,
                                                                   Mesh.one_mon(mesh),
                                                                   fe, Mesh.interior_face,
                                                                   mesh)
  end

  sqrt(integral_sq_errs)
end

function err_vs_proj_L2_norm(exact_sol::Function, wg_sol::WGSolution)
  const basis = wg_sol.basis
  const mesh = basis.mesh

  integral_sq_err = zeroR
  for fe=fenum(1):Mesh.num_fes(mesh)
    const proj_poly = Proj.project_onto_fe_face_supported_approx_subspace_as_poly(exact_sol, fe, Mesh.interior_face, basis)
    const wg_sol_poly = wg_sol_interior_poly(fe, wg_sol)
    const err = proj_poly - wg_sol_poly
    const sq_err = Poly.canonical_form(err * err)
    const fe_oshape = Mesh.oriented_shape_for_fe(fe, mesh)
    integral_sq_err += Mesh.integral_face_rel_on_oshape_face(sq_err,
                                                             fe_oshape, Mesh.interior_face,
                                                             mesh)
  end

  sqrt(integral_sq_err)
end

function err_grad_vs_wgrad_L2_norm(exact_sol_grad::Function, wg_sol::WGSolution)
  const basis = wg_sol.basis
  const mesh = basis.mesh
  const one = Mesh.one_mon(mesh)
  const d = Mesh.space_dim(mesh)
  const fe_rel_x = Array(R, d)
  const fe_origin = Array(R, d)

  integral_sq_err = zeroR
  for fe=fenum(1):Mesh.num_fes(mesh)
    Mesh.fe_interior_origin!(fe, fe_origin, mesh)
    const wgrad = wg_sol_wgrad_on_interior(fe, wg_sol)
    function sq_err(x::Vector{R})
      for i=1:d
        fe_rel_x[i] = x[i] - fe_origin[i]
      end
      const diff = exact_sol_grad(x) - Poly.value_at(wgrad, fe_rel_x)
      dot(diff, diff)
    end
    integral_sq_err += Mesh.integral_global_x_face_rel_on_fe_face(sq_err,
                                                                  one,
                                                                  fe, Mesh.interior_face,
                                                                  mesh)
  end
  sqrt(integral_sq_err)
end

# Compute semi-norm ||| Q_h u - u_h |||_vbf = sqrt(vbf(Q u - u_h, Q u - u_h)),
# where Q_h is piecwise L2 projection onto finite element faces.
function err_vs_proj_vbf_seminorm(exact_sol::Function, wg_sol::WGSolution, vbf::AbstractVariationalBilinearForm)
  const basis = wg_sol.basis
  const mesh = basis.mesh
  const vbf_symm = VBF.is_symmetric(vbf)

  # work arrays
  const side_diffs = Array(Vector{R}, Mesh.max_num_shape_sides(basis.mesh))
  const is_nb_side = Array(Bool, Mesh.max_num_shape_sides(basis.mesh))

  # We must evaluate
  #   sum_{fe} vbf(diff(fe), diff(fe)),
  # where
  #   diff(fe) = diff_int(fe) + sum_{s in nb-sides(fe)} diff_s,
  # and diff_f for a face f is the L2 projection onto f of the exact solution minus the wg
  # solution on the face, and 0 elsewhere.
  # By the bilinearity of the vbf, we need to sum the contributions from each finite element
  # of the vbf applied to the differences on each interior or non-boundary side against that
  # on each interior or non-boundary side.

  sum = zeroR
  for fe=fenum(1):Mesh.num_fes(mesh)
    const fe_oshape = Mesh.oriented_shape_for_fe(fe, mesh)
    const num_sides = Mesh.num_side_faces_for_shape(fe_oshape, mesh)
    # Find non-boundary sides
    for sf=feface_one:num_sides
      is_nb_side[sf] = !Mesh.is_boundary_side(fe, sf, mesh)
    end

    # Compute exact solution projection minus wg solution for all faces.
    const int_diff = Proj.project_onto_fe_face_supported_approx_subspace(exact_sol, fe, Mesh.interior_face, basis) -
                     wg_sol_interior_coefs(fe, wg_sol)
    for sf=feface_one:num_sides if is_nb_side[sf]
      diff = Proj.project_onto_fe_face_supported_approx_subspace(exact_sol, fe, sf, basis)
      diff -= wg_sol_side_coefs(fe, sf, wg_sol)
      side_diffs[sf] = diff
    end end

    # interior vs interior
    sum += VBF.poly_on_face_vs_poly_on_face(fe_oshape,
                                            int_diff, Mesh.interior_face,
                                            int_diff, Mesh.interior_face,
                                            basis,
                                            vbf)

    for sf=feface_one:num_sides if is_nb_side[sf]
      const sf_diff = side_diffs[sf]

      # Add contributions from pairings of a side and interior.

      # interior vs side
      const int_vs_side = VBF.poly_on_face_vs_poly_on_face(fe_oshape,
                                                           int_diff, Mesh.interior_face,
                                                           sf_diff, sf,
                                                           basis,
                                                           vbf)
      sum += int_vs_side

      # side vs interior
      sum += if vbf_symm  int_vs_side
             else VBF.poly_on_face_vs_poly_on_face(fe_oshape,
                                                   sf_diff, sf,
                                                   int_diff, Mesh.interior_face,
                                                   basis,
                                                   vbf) end

      # Add contributions for pairings of sides.

      # side vs side
      if vbf_symm
        # contributions from pairings of unequal sides (symmetric case)
        for sf2=fefacenum(sf+1):num_sides if is_nb_side[sf2]
          sum += 2 * VBF.poly_on_face_vs_poly_on_face(fe_oshape, sf_diff, sf, side_diffs[sf2], sf2, basis, vbf)
        end end
        # contributions from side self-pairings (symmetric case)
        sum += VBF.poly_on_face_vs_poly_on_face(fe_oshape, sf_diff, sf, sf_diff, sf, basis, vbf)
      else # vbf not symmetric
        # contributions for all side vs side pairings (asymmetric case)
        for sf2=feface_one:num_sides if is_nb_side[sf2]
          sum += VBF.poly_on_face_vs_poly_on_face(fe_oshape, sf_diff, sf, side_diffs[sf2], sf2, basis, vbf)
        end end
      end
    end end # non-boundary sides
  end
  sqrt(sum)
end

end
*/
<|endoftext|>