<commit_before>// `cargo bench` works, but if you use `cargo bench -- --save-baseline <name>`
// or pass any other args to it, it fails with the error
// `cargo bench unknown option --save-baseline`.
// To pass args to criterion, use this form
// `cargo bench --features criterion --bench <name of the bench> -- --save-baseline <name>`.

#![allow(unused_imports, dead_code)]

#[cfg(feature = "criterion")]
use criterion::{criterion_group, criterion_main, Criterion};
use ruma_events::{
    room::power_levels::PowerLevelsEventContent, AnyRoomEvent, AnyStateEvent, StateEvent,
};
use ruma_serde::Raw;
use serde_json::json;

fn power_levels() -> serde_json::Value {
    json!({
        "content": {
            "ban": 50,
            "events": {
                "m.room.avatar": 50,
                "m.room.canonical_alias": 50,
                "m.room.history_visibility": 100,
                "m.room.name": 50,
                "m.room.power_levels": 100
            },
            "events_default": 0,
            "invite": 0,
            "kick": 50,
            "redact": 50,
            "state_default": 50,
            "users": {
                "@example:localhost": 100
            },
            "users_default": 0
        },
        "event_id": "$15139375512JaHAW:localhost",
        "origin_server_ts": 45,
        "sender": "@example:localhost",
        "room_id": "!room:localhost",
        "state_key": "",
        "type": "m.room.power_levels",
        "unsigned": {
            "age": 45
        }
    })
}

#[cfg(feature = "criterion")]
fn deserialize_any_room_event(c: &mut Criterion) {
    let json_data = power_levels();

    c.bench_function("deserialize to `AnyRoomEvent`", |b| {
        b.iter(|| {
            let _ = serde_json::from_value::<Raw<AnyRoomEvent>>(json_data.clone())
                .unwrap()
                .deserialize()
                .unwrap();
        })
    });
}

#[cfg(feature = "criterion")]
fn deserialize_any_state_event(c: &mut Criterion) {
    let json_data = power_levels();

    c.bench_function("deserialize to `AnyStateEvent`", |b| {
        b.iter(|| {
            let _ = serde_json::from_value::<Raw<AnyStateEvent>>(json_data.clone())
                .unwrap()
                .deserialize()
                .unwrap();
        })
    });
}

#[cfg(feature = "criterion")]
fn deserialize_specific_event(c: &mut Criterion) {
    let json_data = power_levels();

    c.bench_function("deserialize to `StateEvent<PowerLevelsEventContent>`", |b| {
        b.iter(|| {
            let _ = serde_json::from_value::<Raw<StateEvent<PowerLevelsEventContent>>>(
                json_data.clone(),
            )
            .unwrap()
            .deserialize()
            .unwrap();
        })
    });
}

#[cfg(feature = "criterion")]
criterion_group!(
    benches,
    deserialize_any_event,
    deserialize_any_room_event,
    deserialize_any_state_event,
    deserialize_specific_event
);

#[cfg(feature = "criterion")]
criterion_main!(benches);

#[cfg(not(feature = "criterion"))]
fn main() {
    // To run the benchmarks the "criterion" feature must be enabled use:
    // `cargo bench --features criterion --bench event_deserialize`
    panic!("Enable the criterion feature to run benchmarks");
}
<commit_msg>events: Fix event deserialization benchmark<commit_after>// `cargo bench` works, but if you use `cargo bench -- --save-baseline <name>`
// or pass any other args to it, it fails with the error
// `cargo bench unknown option --save-baseline`.
// To pass args to criterion, use this form
// `cargo bench --features criterion --bench <name of the bench> -- --save-baseline <name>`.

#![allow(unused_imports, dead_code)]

#[cfg(feature = "criterion")]
use criterion::{criterion_group, criterion_main, Criterion};
use ruma_events::{
    room::power_levels::PowerLevelsEventContent, AnyRoomEvent, AnyStateEvent, StateEvent,
};
use ruma_serde::Raw;
use serde_json::json;

fn power_levels() -> serde_json::Value {
    json!({
        "content": {
            "ban": 50,
            "events": {
                "m.room.avatar": 50,
                "m.room.canonical_alias": 50,
                "m.room.history_visibility": 100,
                "m.room.name": 50,
                "m.room.power_levels": 100
            },
            "events_default": 0,
            "invite": 0,
            "kick": 50,
            "redact": 50,
            "state_default": 50,
            "users": {
                "@example:localhost": 100
            },
            "users_default": 0
        },
        "event_id": "$15139375512JaHAW:localhost",
        "origin_server_ts": 45,
        "sender": "@example:localhost",
        "room_id": "!room:localhost",
        "state_key": "",
        "type": "m.room.power_levels",
        "unsigned": {
            "age": 45
        }
    })
}

#[cfg(feature = "criterion")]
fn deserialize_any_room_event(c: &mut Criterion) {
    let json_data = power_levels();

    c.bench_function("deserialize to `AnyRoomEvent`", |b| {
        b.iter(|| {
            let _ = serde_json::from_value::<Raw<AnyRoomEvent>>(json_data.clone())
                .unwrap()
                .deserialize()
                .unwrap();
        })
    });
}

#[cfg(feature = "criterion")]
fn deserialize_any_state_event(c: &mut Criterion) {
    let json_data = power_levels();

    c.bench_function("deserialize to `AnyStateEvent`", |b| {
        b.iter(|| {
            let _ = serde_json::from_value::<Raw<AnyStateEvent>>(json_data.clone())
                .unwrap()
                .deserialize()
                .unwrap();
        })
    });
}

#[cfg(feature = "criterion")]
fn deserialize_specific_event(c: &mut Criterion) {
    let json_data = power_levels();

    c.bench_function("deserialize to `StateEvent<PowerLevelsEventContent>`", |b| {
        b.iter(|| {
            let _ = serde_json::from_value::<Raw<StateEvent<PowerLevelsEventContent>>>(
                json_data.clone(),
            )
            .unwrap()
            .deserialize()
            .unwrap();
        })
    });
}

#[cfg(feature = "criterion")]
criterion_group!(
    benches,
    deserialize_any_room_event,
    deserialize_any_state_event,
    deserialize_specific_event
);

#[cfg(feature = "criterion")]
criterion_main!(benches);

#[cfg(not(feature = "criterion"))]
fn main() {
    // To run the benchmarks the "criterion" feature must be enabled use:
    // `cargo bench --features criterion --bench event_deserialize`
    panic!("Enable the criterion feature to run benchmarks");
}
<|endoftext|>


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

//! Windows console handling

// FIXME (#13400): this is only a tiny fraction of the Windows console api

extern crate winapi;

use std::io::prelude::*;
use std::io;
use std::ptr;
use Attr;
use Error;
use Result;
use Terminal;
use color;

use win::winapi::um::wincon::{SetConsoleCursorPosition, SetConsoleTextAttribute};
use win::winapi::um::wincon::{FillConsoleOutputCharacterW, GetConsoleScreenBufferInfo, COORD};
use win::winapi::um::wincon::FillConsoleOutputAttribute;
use win::winapi::shared::minwindef::{DWORD, WORD};
use win::winapi::um::handleapi::INVALID_HANDLE_VALUE;
use win::winapi::um::fileapi::{CreateFileA, OPEN_EXISTING};
use win::winapi::um::winnt::{FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE};

/// A Terminal implementation which uses the Win32 Console API.
pub struct WinConsole<T> {
    buf: T,
    def_foreground: color::Color,
    def_background: color::Color,
    foreground: color::Color,
    background: color::Color,
}

fn color_to_bits(color: color::Color) -> u16 {
    // magic numbers from mingw-w64's wincon.h

    let bits = match color % 8 {
        color::BLACK => 0,
        color::BLUE => 0x1,
        color::GREEN => 0x2,
        color::RED => 0x4,
        color::YELLOW => 0x2 | 0x4,
        color::MAGENTA => 0x1 | 0x4,
        color::CYAN => 0x1 | 0x2,
        color::WHITE => 0x1 | 0x2 | 0x4,
        _ => unreachable!(),
    };

    if color >= 8 {
        bits | 0x8
    } else {
        bits
    }
}

fn bits_to_color(bits: u16) -> color::Color {
    let color = match bits & 0x7 {
        0 => color::BLACK,
        0x1 => color::BLUE,
        0x2 => color::GREEN,
        0x4 => color::RED,
        0x6 => color::YELLOW,
        0x5 => color::MAGENTA,
        0x3 => color::CYAN,
        0x7 => color::WHITE,
        _ => unreachable!(),
    };

    color | (bits as u32 & 0x8) // copy the hi-intensity bit
}

// Just get a handle to the current console buffer whatever it is
fn conout() -> io::Result<HANDLE> {
    let name = b"CONOUT$\0";
    let handle = unsafe {
        CreateFileA(
            name.as_ptr() as *const i8,
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_WRITE,
            ptr::null_mut(),
            OPEN_EXISTING,
            0,
            ptr::null_mut(),
        )
    };
    if handle == INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else {
        Ok(handle)
    }
}

// This test will only pass if it is running in an actual console, probably
#[test]
fn test_conout() {
    assert!(conout().is_ok())
}

impl<T: Write + Send> WinConsole<T> {
    fn apply(&mut self) -> io::Result<()> {
        let out = conout()?;
        let _unused = self.buf.flush();
        let mut accum: WORD = 0;
        accum |= color_to_bits(self.foreground);
        accum |= color_to_bits(self.background) << 4;
        unsafe {
            SetConsoleTextAttribute(out, accum);
        }
        Ok(())
    }

    /// Returns `Err` whenever the terminal cannot be created for some
    /// reason.
    pub fn new(out: T) -> io::Result<WinConsole<T>> {
        let fg;
        let bg;
        let handle = conout()?;
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                fg = bits_to_color(buffer_info.wAttributes);
                bg = bits_to_color(buffer_info.wAttributes >> 4);
            } else {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(WinConsole {
            buf: out,
            def_foreground: fg,
            def_background: bg,
            foreground: fg,
            background: bg,
        })
    }
}

impl<T: Write> Write for WinConsole<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.buf.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.buf.flush()
    }
}

impl<T: Write + Send> Terminal for WinConsole<T> {
    type Output = T;

    fn fg(&mut self, color: color::Color) -> Result<()> {
        self.foreground = color;
        self.apply()?;

        Ok(())
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        self.background = color;
        self.apply()?;

        Ok(())
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(f) => {
                self.foreground = f;
                self.apply()?;
                Ok(())
            }
            Attr::BackgroundColor(b) => {
                self.background = b;
                self.apply()?;
                Ok(())
            }
            _ => Err(Error::NotSupported),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        // it claims support for underscore and reverse video, but I can't get
        // it to do anything -cmr
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true,
            _ => false,
        }
    }

    fn reset(&mut self) -> Result<()> {
        self.foreground = self.def_foreground;
        self.background = self.def_background;
        self.apply()?;

        Ok(())
    }

    fn supports_reset(&self) -> bool {
        true
    }

    fn supports_color(&self) -> bool {
        true
    }

    fn cursor_up(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = conout()?;
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                let (x, y) = (
                    buffer_info.dwCursorPosition.X,
                    buffer_info.dwCursorPosition.Y,
                );
                if y == 0 {
                    // Even though this might want to be a CursorPositionInvalid, on Unix there
                    // is no checking to see if the cursor is already on the first line.
                    // I'm not sure what the ideal behavior is, but I think it'd be silly to have
                    // cursor_up fail in this case.
                    Ok(())
                } else {
                    let pos = COORD { X: x, Y: y - 1 };
                    if SetConsoleCursorPosition(handle, pos) != 0 {
                        Ok(())
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn delete_line(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = conout()?;
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if GetConsoleScreenBufferInfo(handle, &mut buffer_info) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            let pos = buffer_info.dwCursorPosition;
            let size = buffer_info.dwSize;
            let num = (size.X - pos.X) as DWORD;
            let mut written = 0;
            // 0x0020u16 is ' ' (space) in UTF-16 (same as ascii)
            if FillConsoleOutputCharacterW(handle, 0x0020, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            if FillConsoleOutputAttribute(handle, 0, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            // Similar reasoning for not failing as in cursor_up -- it doesn't even make
            // sense to
            // me that these APIs could have written 0, unless the terminal is width zero.
            Ok(())
        }
    }

    fn carriage_return(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = conout()?;
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                let COORD { X: x, Y: y } = buffer_info.dwCursorPosition;
                if x == 0 {
                    Err(Error::CursorDestinationInvalid)
                } else {
                    let pos = COORD { X: 0, Y: y };
                    if SetConsoleCursorPosition(handle, pos) != 0 {
                        Ok(())
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn get_ref<'a>(&'a self) -> &'a T {
        &self.buf
    }

    fn get_mut<'a>(&'a mut self) -> &'a mut T {
        &mut self.buf
    }

    fn into_inner(self) -> T
    where
        Self: Sized,
    {
        self.buf
    }
}
<commit_msg>don't leak memory on Windows<commit_after>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Windows console handling

// FIXME (#13400): this is only a tiny fraction of the Windows console api

extern crate winapi;

use std::io::prelude::*;
use std::io;
use std::ops::Deref;
use std::ptr;
use Attr;
use Error;
use Result;
use Terminal;
use color;

use win::winapi::um::wincon::{SetConsoleCursorPosition, SetConsoleTextAttribute};
use win::winapi::um::wincon::{FillConsoleOutputCharacterW, GetConsoleScreenBufferInfo, COORD};
use win::winapi::um::wincon::FillConsoleOutputAttribute;
use win::winapi::shared::minwindef::{DWORD, WORD};
use win::winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use win::winapi::um::fileapi::{CreateFileA, OPEN_EXISTING};
use win::winapi::um::winnt::{FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE};

/// A Terminal implementation which uses the Win32 Console API.
pub struct WinConsole<T> {
    buf: T,
    def_foreground: color::Color,
    def_background: color::Color,
    foreground: color::Color,
    background: color::Color,
}

fn color_to_bits(color: color::Color) -> u16 {
    // magic numbers from mingw-w64's wincon.h

    let bits = match color % 8 {
        color::BLACK => 0,
        color::BLUE => 0x1,
        color::GREEN => 0x2,
        color::RED => 0x4,
        color::YELLOW => 0x2 | 0x4,
        color::MAGENTA => 0x1 | 0x4,
        color::CYAN => 0x1 | 0x2,
        color::WHITE => 0x1 | 0x2 | 0x4,
        _ => unreachable!(),
    };

    if color >= 8 {
        bits | 0x8
    } else {
        bits
    }
}

fn bits_to_color(bits: u16) -> color::Color {
    let color = match bits & 0x7 {
        0 => color::BLACK,
        0x1 => color::BLUE,
        0x2 => color::GREEN,
        0x4 => color::RED,
        0x6 => color::YELLOW,
        0x5 => color::MAGENTA,
        0x3 => color::CYAN,
        0x7 => color::WHITE,
        _ => unreachable!(),
    };

    color | (bits as u32 & 0x8) // copy the hi-intensity bit
}

struct HandleWrapper {
    inner: HANDLE,
}

impl HandleWrapper {
    fn new(h: HANDLE) -> HandleWrapper {
        HandleWrapper { inner: h }
    }
}

impl Drop for HandleWrapper {
    fn drop(&mut self) {
        if self.inner != INVALID_HANDLE_VALUE {
            unsafe {
                CloseHandle(self.inner);
            }
        }
    }
}

impl Deref for HandleWrapper {
    type Target = HANDLE;
    fn deref(&self) -> &HANDLE {
        &self.inner
    }
}

// Just get a handle to the current console buffer whatever it is
fn conout() -> io::Result<HandleWrapper> {
    let name = b"CONOUT$\0";
    let handle = unsafe {
        CreateFileA(
            name.as_ptr() as *const i8,
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_WRITE,
            ptr::null_mut(),
            OPEN_EXISTING,
            0,
            ptr::null_mut(),
        )
    };
    if handle == INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else {
        Ok(HandleWrapper::new(handle))
    }
}

// This test will only pass if it is running in an actual console, probably
#[test]
fn test_conout() {
    assert!(conout().is_ok())
}

impl<T: Write + Send> WinConsole<T> {
    fn apply(&mut self) -> io::Result<()> {
        let out = conout()?;
        let _unused = self.buf.flush();
        let mut accum: WORD = 0;
        accum |= color_to_bits(self.foreground);
        accum |= color_to_bits(self.background) << 4;
        unsafe {
            SetConsoleTextAttribute(*out, accum);
        }
        Ok(())
    }

    /// Returns `Err` whenever the terminal cannot be created for some
    /// reason.
    pub fn new(out: T) -> io::Result<WinConsole<T>> {
        let fg;
        let bg;
        let handle = conout()?;
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if GetConsoleScreenBufferInfo(*handle, &mut buffer_info) != 0 {
                fg = bits_to_color(buffer_info.wAttributes);
                bg = bits_to_color(buffer_info.wAttributes >> 4);
            } else {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(WinConsole {
            buf: out,
            def_foreground: fg,
            def_background: bg,
            foreground: fg,
            background: bg,
        })
    }
}

impl<T: Write> Write for WinConsole<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.buf.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.buf.flush()
    }
}

impl<T: Write + Send> Terminal for WinConsole<T> {
    type Output = T;

    fn fg(&mut self, color: color::Color) -> Result<()> {
        self.foreground = color;
        self.apply()?;

        Ok(())
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        self.background = color;
        self.apply()?;

        Ok(())
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(f) => {
                self.foreground = f;
                self.apply()?;
                Ok(())
            }
            Attr::BackgroundColor(b) => {
                self.background = b;
                self.apply()?;
                Ok(())
            }
            _ => Err(Error::NotSupported),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        // it claims support for underscore and reverse video, but I can't get
        // it to do anything -cmr
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true,
            _ => false,
        }
    }

    fn reset(&mut self) -> Result<()> {
        self.foreground = self.def_foreground;
        self.background = self.def_background;
        self.apply()?;

        Ok(())
    }

    fn supports_reset(&self) -> bool {
        true
    }

    fn supports_color(&self) -> bool {
        true
    }

    fn cursor_up(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = conout()?;
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if GetConsoleScreenBufferInfo(*handle, &mut buffer_info) != 0 {
                let (x, y) = (
                    buffer_info.dwCursorPosition.X,
                    buffer_info.dwCursorPosition.Y,
                );
                if y == 0 {
                    // Even though this might want to be a CursorPositionInvalid, on Unix there
                    // is no checking to see if the cursor is already on the first line.
                    // I'm not sure what the ideal behavior is, but I think it'd be silly to have
                    // cursor_up fail in this case.
                    Ok(())
                } else {
                    let pos = COORD { X: x, Y: y - 1 };
                    if SetConsoleCursorPosition(*handle, pos) != 0 {
                        Ok(())
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn delete_line(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = conout()?;
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if GetConsoleScreenBufferInfo(*handle, &mut buffer_info) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            let pos = buffer_info.dwCursorPosition;
            let size = buffer_info.dwSize;
            let num = (size.X - pos.X) as DWORD;
            let mut written = 0;
            // 0x0020u16 is ' ' (space) in UTF-16 (same as ascii)
            if FillConsoleOutputCharacterW(*handle, 0x0020, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            if FillConsoleOutputAttribute(*handle, 0, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            // Similar reasoning for not failing as in cursor_up -- it doesn't even make
            // sense to
            // me that these APIs could have written 0, unless the terminal is width zero.
            Ok(())
        }
    }

    fn carriage_return(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = conout()?;
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if GetConsoleScreenBufferInfo(*handle, &mut buffer_info) != 0 {
                let COORD { X: x, Y: y } = buffer_info.dwCursorPosition;
                if x == 0 {
                    Err(Error::CursorDestinationInvalid)
                } else {
                    let pos = COORD { X: 0, Y: y };
                    if SetConsoleCursorPosition(*handle, pos) != 0 {
                        Ok(())
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn get_ref<'a>(&'a self) -> &'a T {
        &self.buf
    }

    fn get_mut<'a>(&'a mut self) -> &'a mut T {
        &mut self.buf
    }

    fn into_inner(self) -> T
    where
        Self: Sized,
    {
        self.buf
    }
}
<|endoftext|>


<commit_before>use crate::shapes::{IntegPointData, Shape, StateOfShape};
use crate::util::AsArray1D;
use crate::StrError;
use russell_lab::Vector;

/// Implements the shape(N) times scalar(S) integration case
///
/// Interpolation functions times scalar field:
///
/// ```text
///      ⌠    → →     →
/// aᵐ = │ Nᵐ(x(ξ)) s(x) tₕ dΩ
///      ⌡
///      Ωₑ
/// ```
///
/// or, for lines in multi-dimensions:
///
/// ```text
///      ⌠
/// aᵐ = │ Nᵐ(ℓ(ξ)) s(ℓ) tₕ dℓ
///      ⌡
///      Γₑ
/// ```
///
/// The numerical integration is:
///
/// ```text
///      nip-1     →     →          →
/// aᵐ ≈   Σ    Nᵐ(ιᵖ) s(ιᵖ) tₕ |J|(ιᵖ) wᵖ
///       p=0
/// ```
///
/// # Output
///
/// ```text
///     ┌     ┐
///     |  a⁰ |
///     |  a¹ |
/// a = |  a² |
///     | ··· |
///     |  aᵐ |
///     └     ┘
/// ```
///
/// * `a` -- A vector containing all `aᵐ` values, one after another, and
///          sequentially placed as shown above. `m` is the index of the node.
///          The length of `a` must be equal to `nnode`.
///
/// # Updated
///
/// * `state` -- Will be updated by the Shape functions
///
/// # Input
///
/// * `shape` -- Shape functions
/// * `ips` -- Integration points (n_integ_point)
/// * `s` -- All values produced by `s(x(ιᵖ))` (n_integ_point)
/// * `erase_a` -- Fills `a` vector with zeros, otherwise accumulate values into `a`
/// * `th` -- The out-of-plane thickness (`tₕ`) in 2D. Use 1.0 for 3D or for plane-stress models.
pub fn shape_times_scalar<'a, T>(
    a: &mut Vector,
    state: &mut StateOfShape,
    shape: &Shape,
    ips: IntegPointData,
    s: &'a T,
    th: f64,
    erase_a: bool,
) -> Result<(), StrError>
where
    T: AsArray1D<'a, f64>,
{
    // check
    if a.dim() != shape.nnode {
        return Err("a.len() must be equal to nnode");
    }

    // clear output vector
    if erase_a {
        a.fill(0.0);
    }

    // loop over integration points
    for index in 0..ips.len() {
        // ksi coordinates and weight
        let iota = &ips[index];
        let weight = ips[index][3];

        // calculate interpolation functions and Jacobian
        shape.calc_interp(state, iota)?;
        let det_jac = shape.calc_jacobian(state, iota)?;

        // loop over nodes and perform sum
        for m in 0..shape.nnode {
            a[m] += state.interp[m] * s.at(index) * th * det_jac * weight;
        }
    }
    Ok(())
}

/// Implements the the shape(N) times vector(V) integration case
///
/// Interpolation functions times vector field:
///
/// ```text
/// →    ⌠    → →   → →
/// bᵐ = │ Nᵐ(x(ξ)) v(x) tₕ dΩ
///      ⌡
///      Ωₑ
/// ```
///
/// The numerical integration is:
///
/// ```text
/// →    nip-1     →   → →          →
/// bᵐ ≈   Σ    Nᵐ(ιᵖ) v(ιᵖ) tₕ |J|(ιᵖ) wᵖ
///       p=0
/// ```
///
/// # Output
///
/// ```text
///     ┌     ┐
///     | b⁰₀ |
///     | b⁰₁ |
///     | b¹₀ |
/// b = | b¹₁ |
///     | b²₀ |
///     | b²₁ |
///     | ··· |
///     | bᵐᵢ |  ⟸  ii := i + m * space_ndim
///     └     ┘
///
/// m = ii / space_ndim
/// i = ii % space_ndim
/// ```
///
/// * `b` -- A vector containing all `bᵐᵢ` values, one after another, and sequentially placed
///          as shown above (in 2D). `m` is the index of the node and `i` corresponds to `space_ndim`.
///          The length of `b` must be equal to `nnode * space_ndim`.
///
/// # Updated
///
/// * `state` -- Will be updated by the Shape functions
///
/// # Input
///
/// * `shape` -- Shape functions
/// * `ips` -- Integration points (n_integ_point)
/// * `v` -- All values produced by `v(x(ιᵖ))` (n_integ_point)
/// * `erase_b` -- fills `b` vector with zeros, otherwise accumulate values into `b`
/// * `th` -- tₕ the out-of-plane thickness in 2D or 1.0 otherwise (e.g., for plane-stress models)
pub fn shape_times_vector(
    b: &mut Vector,
    state: &mut StateOfShape,
    shape: &Shape,
    ips: IntegPointData,
    v: &Vec<Vector>,
    th: f64,
    erase_b: bool,
) -> Result<(), StrError> {
    // check
    if b.dim() != shape.nnode * shape.space_ndim {
        return Err("b.len() must be equal to nnode * space_ndim");
    }

    // clear output vector
    if erase_b {
        b.fill(0.0);
    }

    // loop over integration points
    for index in 0..ips.len() {
        // ksi coordinates and weight
        let iota = &ips[index];
        let weight = ips[index][3];

        // calculate interpolation functions and Jacobian
        shape.calc_interp(state, iota)?;
        let det_jac = shape.calc_jacobian(state, iota)?;

        // add contribution to b vector
        let coef = th * det_jac * weight;
        let nn = &state.interp;
        if shape.space_ndim == 1 {
            for m in 0..shape.nnode {
                b[m] += coef * nn[m] * v[index][0];
            }
        } else if shape.space_ndim == 2 {
            for m in 0..shape.nnode {
                b[0 + m * 2] += coef * nn[m] * v[index][0];
                b[1 + m * 2] += coef * nn[m] * v[index][1];
            }
        } else {
            for m in 0..shape.nnode {
                b[0 + m * 3] += coef * nn[m] * v[index][0];
                b[1 + m * 3] += coef * nn[m] * v[index][1];
                b[2 + m * 3] += coef * nn[m] * v[index][2];
            }
        }
    }
    Ok(())
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::{shape_times_scalar, shape_times_vector};
    use crate::shapes::{Shape, StateOfShape, Verification, IP_LIN_LEGENDRE_2, IP_TRI_INTERNAL_1};
    use crate::StrError;
    use russell_chk::assert_vec_approx_eq;
    use russell_lab::Vector;

    // to test if variables are cleared before sum
    const NOISE: f64 = 1234.56;

    #[test]
    fn capture_some_errors() {
        let shape = Shape::new(2, 1, 2).unwrap();
        let mut state = StateOfShape::new(shape.geo_ndim, &[[0.0, 0.0], [1.0, 0.0]]).unwrap();
        let mut a = Vector::new(3);
        assert_eq!(
            shape_times_scalar(&mut a, &mut state, &shape, &[], &[], 1.0, false).err(),
            Some("a.len() must be equal to nnode")
        );
    }

    #[test]
    fn shape_times_scalar_works_lin2() -> Result<(), StrError> {
        // lin2 with linear source term:
        //
        // s(x) = x
        //
        // solution:
        //
        //        ┌           ┐
        //      l │ 2 xa + xb │
        // Fₛ = — │           │
        //      6 │ xa + 2 xb │
        //        └           ┘
        let l = 6.0;
        let (shape, mut state) = Verification::line_segment_lin2(l);
        let ips = &IP_LIN_LEGENDRE_2;
        let x_ips = shape.calc_integ_points_coords(&mut state, ips)?;
        let s: Vec<_> = x_ips.iter().map(|x| x[0]).collect();
        let mut a = Vector::filled(shape.nnode, NOISE);
        shape_times_scalar(&mut a, &mut state, &shape, ips, &s, 1.0, true)?;
        let cf = l / 6.0;
        let (xa, xb) = (state.coords_transp[0][0], state.coords_transp[0][1]);
        let a_correct = &[cf * (2.0 * xa + xb), cf * (xa + 2.0 * xb)];
        assert_vec_approx_eq!(a.as_data(), a_correct, 1e-15);
        Ok(())
    }

    #[test]
    fn shape_times_scalar_works_tri3() -> Result<(), StrError> {
        // tri3 with a constant source term:
        //
        // s(x) = cₛ
        //
        // solution:
        //           ┌   ┐
        //      cₛ A │ 1 │
        // Fₛ = ———— │ 1 │
        //        3  │ 1 │
        //           └   ┘
        let l = 5.0;
        let (shape, mut state, area) = Verification::equilateral_triangle_tri3(l);
        let ips = &IP_TRI_INTERNAL_1;
        const CS: f64 = 3.0;
        let s: Vec<_> = (0..ips.len()).map(|_| CS).collect();
        let mut a = Vector::filled(shape.nnode, NOISE);
        shape_times_scalar(&mut a, &mut state, &shape, ips, &s, 1.0, true)?;
        let cf = CS * area / 3.0;
        let a_correct = &[cf, cf, cf];
        assert_vec_approx_eq!(a.as_data(), a_correct, 1e-14);
        Ok(())
    }

    #[test]
    fn shape_times_vector_works_lin2() -> Result<(), StrError> {
        // This test is similar to the shape_times_scalar with lin2, however using a vector
        // So, each component of `b` equals `Fₛ`
        let l = 6.0;
        let (shape, mut state) = Verification::line_segment_lin2(l);
        let ips = &IP_LIN_LEGENDRE_2;
        let x_ips = shape.calc_integ_points_coords(&mut state, ips)?;
        let v: Vec<_> = x_ips.iter().map(|x| Vector::filled(shape.space_ndim, x[0])).collect();
        let mut b = Vector::filled(shape.nnode * shape.space_ndim, NOISE);
        shape_times_vector(&mut b, &mut state, &shape, ips, &v, 1.0, true)?;
        let cf = l / 6.0;
        let (xa, xb) = (state.coords_transp[0][0], state.coords_transp[0][1]);
        let b_correct = &[
            cf * (2.0 * xa + xb),
            cf * (2.0 * xa + xb),
            cf * (xa + 2.0 * xb),
            cf * (xa + 2.0 * xb),
        ];
        assert_vec_approx_eq!(b.as_data(), b_correct, 1e-15);
        Ok(())
    }

    #[test]
    fn shape_times_vector_works_tri3() -> Result<(), StrError> {
        // This test is similar to the shape_times_scalar with tri3, however using a vector
        // So, each component of `b` equals `Fₛ`
        let l = 5.0;
        let (shape, mut state, area) = Verification::equilateral_triangle_tri3(l);
        let ips = &IP_TRI_INTERNAL_1;
        const CS: f64 = 3.0;
        let v: Vec<_> = (0..ips.len()).map(|_| Vector::filled(shape.space_ndim, CS)).collect();
        let mut b = Vector::filled(shape.nnode * shape.space_ndim, NOISE);
        shape_times_vector(&mut b, &mut state, &shape, ips, &v, 1.0, true)?;
        let cf = CS * area / 3.0;
        let b_correct = &[cf, cf, cf, cf, cf, cf];
        assert_vec_approx_eq!(b.as_data(), b_correct, 1e-14);
        Ok(())
    }
}
<commit_msg>Improve comment<commit_after>use crate::shapes::{IntegPointData, Shape, StateOfShape};
use crate::util::AsArray1D;
use crate::StrError;
use russell_lab::Vector;

/// Implements the shape(N) times scalar(S) integration case
///
/// Interpolation functions times scalar field:
///
/// ```text
///      ⌠    → →     →
/// aᵐ = │ Nᵐ(x(ξ)) s(x) tₕ dΩ
///      ⌡
///      Ωₑ
/// ```
///
/// or, for lines in multi-dimensions:
///
/// ```text
///      ⌠
/// aᵐ = │ Nᵐ(ℓ(ξ)) s(ℓ) tₕ dℓ
///      ⌡
///      Γₑ
/// ```
///
/// The numerical integration is:
///
/// ```text
///      nip-1     →     →          →
/// aᵐ ≈   Σ    Nᵐ(ιᵖ) s(ιᵖ) tₕ |J|(ιᵖ) wᵖ
///       p=0
/// ```
///
/// # Output
///
/// ```text
///     ┌     ┐
///     |  a⁰ |
///     |  a¹ |
/// a = |  a² |
///     | ··· |
///     |  aᵐ |
///     └     ┘
/// ```
///
/// * `a` -- A vector containing all `aᵐ` values, one after another, and
///          sequentially placed as shown above. `m` is the index of the node.
///          The length of `a` must be equal to `nnode`.
///
/// # Updated
///
/// * `state` -- Will be updated by the Shape functions
///
/// # Input
///
/// * `shape` -- Shape functions
/// * `ips` -- Integration points (n_integ_point)
/// * `s` -- All values produced by `s(x(ιᵖ))` (n_integ_point)
/// * `erase_a` -- Fills `a` vector with zeros, otherwise accumulate values into `a`
/// * `th` -- The out-of-plane thickness (`tₕ`) in 2D. Use 1.0 for 3D or for plane-stress models.
pub fn shape_times_scalar<'a, T>(
    a: &mut Vector,
    state: &mut StateOfShape,
    shape: &Shape,
    ips: IntegPointData,
    s: &'a T,
    th: f64,
    erase_a: bool,
) -> Result<(), StrError>
where
    T: AsArray1D<'a, f64>,
{
    // check
    if a.dim() != shape.nnode {
        return Err("a.len() must be equal to nnode");
    }

    // clear output vector
    if erase_a {
        a.fill(0.0);
    }

    // loop over integration points
    for index in 0..ips.len() {
        // ksi coordinates and weight
        let iota = &ips[index];
        let weight = ips[index][3];

        // calculate interpolation functions and Jacobian
        shape.calc_interp(state, iota)?;
        let det_jac = shape.calc_jacobian(state, iota)?;

        // loop over nodes and perform sum
        for m in 0..shape.nnode {
            a[m] += state.interp[m] * s.at(index) * th * det_jac * weight;
        }
    }
    Ok(())
}

/// Implements the the shape(N) times vector(V) integration case
///
/// Interpolation functions times vector field:
///
/// ```text
/// →    ⌠    → →   → →
/// bᵐ = │ Nᵐ(x(ξ)) v(x) tₕ dΩ
///      ⌡
///      Ωₑ
/// ```
///
/// The numerical integration is:
///
/// ```text
/// →    nip-1     →   → →          →
/// bᵐ ≈   Σ    Nᵐ(ιᵖ) v(ιᵖ) tₕ |J|(ιᵖ) wᵖ
///       p=0
/// ```
///
/// # Output
///
/// ```text
///     ┌     ┐
///     | b⁰₀ |
///     | b⁰₁ |
///     | b¹₀ |
/// b = | b¹₁ |
///     | b²₀ |
///     | b²₁ |
///     | ··· |
///     | bᵐᵢ |  ⟸  ii := i + m * space_ndim
///     └     ┘
///
/// m = ii / space_ndim
/// i = ii % space_ndim
/// ```
///
/// * `b` -- A vector containing all `bᵐᵢ` values, one after another, and sequentially placed
///          as shown above (in 2D). `m` is the index of the node and `i` corresponds to `space_ndim`.
///          The length of `b` must be equal to `nnode * space_ndim`.
///
/// # Updated
///
/// * `state` -- Will be updated by the Shape functions
///
/// # Input
///
/// * `shape` -- Shape functions
/// * `ips` -- Integration points (n_integ_point)
/// * `v` -- All values produced by `v(x(ιᵖ))` (n_integ_point); each v has len = space_ndim
/// * `erase_b` -- fills `b` vector with zeros, otherwise accumulate values into `b`
/// * `th` -- tₕ the out-of-plane thickness in 2D or 1.0 otherwise (e.g., for plane-stress models)
pub fn shape_times_vector(
    b: &mut Vector,
    state: &mut StateOfShape,
    shape: &Shape,
    ips: IntegPointData,
    v: &Vec<Vector>,
    th: f64,
    erase_b: bool,
) -> Result<(), StrError> {
    // check
    if b.dim() != shape.nnode * shape.space_ndim {
        return Err("b.len() must be equal to nnode * space_ndim");
    }

    // clear output vector
    if erase_b {
        b.fill(0.0);
    }

    // loop over integration points
    for index in 0..ips.len() {
        // ksi coordinates and weight
        let iota = &ips[index];
        let weight = ips[index][3];

        // calculate interpolation functions and Jacobian
        shape.calc_interp(state, iota)?;
        let det_jac = shape.calc_jacobian(state, iota)?;

        // add contribution to b vector
        let coef = th * det_jac * weight;
        let nn = &state.interp;
        if shape.space_ndim == 1 {
            for m in 0..shape.nnode {
                b[m] += coef * nn[m] * v[index][0];
            }
        } else if shape.space_ndim == 2 {
            for m in 0..shape.nnode {
                b[0 + m * 2] += coef * nn[m] * v[index][0];
                b[1 + m * 2] += coef * nn[m] * v[index][1];
            }
        } else {
            for m in 0..shape.nnode {
                b[0 + m * 3] += coef * nn[m] * v[index][0];
                b[1 + m * 3] += coef * nn[m] * v[index][1];
                b[2 + m * 3] += coef * nn[m] * v[index][2];
            }
        }
    }
    Ok(())
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::{shape_times_scalar, shape_times_vector};
    use crate::shapes::{Shape, StateOfShape, Verification, IP_LIN_LEGENDRE_2, IP_TRI_INTERNAL_1};
    use crate::StrError;
    use russell_chk::assert_vec_approx_eq;
    use russell_lab::Vector;

    // to test if variables are cleared before sum
    const NOISE: f64 = 1234.56;

    #[test]
    fn capture_some_errors() {
        let shape = Shape::new(2, 1, 2).unwrap();
        let mut state = StateOfShape::new(shape.geo_ndim, &[[0.0, 0.0], [1.0, 0.0]]).unwrap();
        let mut a = Vector::new(3);
        assert_eq!(
            shape_times_scalar(&mut a, &mut state, &shape, &[], &[], 1.0, false).err(),
            Some("a.len() must be equal to nnode")
        );
    }

    #[test]
    fn shape_times_scalar_works_lin2() -> Result<(), StrError> {
        // lin2 with linear source term:
        //
        // s(x) = x
        //
        // solution:
        //
        //        ┌           ┐
        //      l │ 2 xa + xb │
        // Fₛ = — │           │
        //      6 │ xa + 2 xb │
        //        └           ┘
        let l = 6.0;
        let (shape, mut state) = Verification::line_segment_lin2(l);
        let ips = &IP_LIN_LEGENDRE_2;
        let x_ips = shape.calc_integ_points_coords(&mut state, ips)?;
        let s: Vec<_> = x_ips.iter().map(|x| x[0]).collect();
        let mut a = Vector::filled(shape.nnode, NOISE);
        shape_times_scalar(&mut a, &mut state, &shape, ips, &s, 1.0, true)?;
        let cf = l / 6.0;
        let (xa, xb) = (state.coords_transp[0][0], state.coords_transp[0][1]);
        let a_correct = &[cf * (2.0 * xa + xb), cf * (xa + 2.0 * xb)];
        assert_vec_approx_eq!(a.as_data(), a_correct, 1e-15);
        Ok(())
    }

    #[test]
    fn shape_times_scalar_works_tri3() -> Result<(), StrError> {
        // tri3 with a constant source term:
        //
        // s(x) = cₛ
        //
        // solution:
        //           ┌   ┐
        //      cₛ A │ 1 │
        // Fₛ = ———— │ 1 │
        //        3  │ 1 │
        //           └   ┘
        let l = 5.0;
        let (shape, mut state, area) = Verification::equilateral_triangle_tri3(l);
        let ips = &IP_TRI_INTERNAL_1;
        const CS: f64 = 3.0;
        let s: Vec<_> = (0..ips.len()).map(|_| CS).collect();
        let mut a = Vector::filled(shape.nnode, NOISE);
        shape_times_scalar(&mut a, &mut state, &shape, ips, &s, 1.0, true)?;
        let cf = CS * area / 3.0;
        let a_correct = &[cf, cf, cf];
        assert_vec_approx_eq!(a.as_data(), a_correct, 1e-14);
        Ok(())
    }

    #[test]
    fn shape_times_vector_works_lin2() -> Result<(), StrError> {
        // This test is similar to the shape_times_scalar with lin2, however using a vector
        // So, each component of `b` equals `Fₛ`
        let l = 6.0;
        let (shape, mut state) = Verification::line_segment_lin2(l);
        let ips = &IP_LIN_LEGENDRE_2;
        let x_ips = shape.calc_integ_points_coords(&mut state, ips)?;
        let v: Vec<_> = x_ips.iter().map(|x| Vector::filled(shape.space_ndim, x[0])).collect();
        let mut b = Vector::filled(shape.nnode * shape.space_ndim, NOISE);
        shape_times_vector(&mut b, &mut state, &shape, ips, &v, 1.0, true)?;
        let cf = l / 6.0;
        let (xa, xb) = (state.coords_transp[0][0], state.coords_transp[0][1]);
        let b_correct = &[
            cf * (2.0 * xa + xb),
            cf * (2.0 * xa + xb),
            cf * (xa + 2.0 * xb),
            cf * (xa + 2.0 * xb),
        ];
        assert_vec_approx_eq!(b.as_data(), b_correct, 1e-15);
        Ok(())
    }

    #[test]
    fn shape_times_vector_works_tri3() -> Result<(), StrError> {
        // This test is similar to the shape_times_scalar with tri3, however using a vector
        // So, each component of `b` equals `Fₛ`
        let l = 5.0;
        let (shape, mut state, area) = Verification::equilateral_triangle_tri3(l);
        let ips = &IP_TRI_INTERNAL_1;
        const CS: f64 = 3.0;
        let v: Vec<_> = (0..ips.len()).map(|_| Vector::filled(shape.space_ndim, CS)).collect();
        let mut b = Vector::filled(shape.nnode * shape.space_ndim, NOISE);
        shape_times_vector(&mut b, &mut state, &shape, ips, &v, 1.0, true)?;
        let cf = CS * area / 3.0;
        let b_correct = &[cf, cf, cf, cf, cf, cf];
        assert_vec_approx_eq!(b.as_data(), b_correct, 1e-14);
        Ok(())
    }
}
<|endoftext|>


<commit_before>#![macro_escape]

#[macro_export]
macro_rules! msg_send(
	($obj:expr $name:ident) => ({
		use runtime::ToMessage;
		let sel_name = stringify!($name);
		let sel = ::runtime::Sel::register(sel_name);
		::runtime::objc_msgSend($obj.as_ptr(), sel)
	});
	($obj:expr $($name:ident : $arg:expr)+) => ({
		use runtime::ToMessage;
		let sel_name = concat!($(stringify!($name), ':'),+);
		let sel = ::runtime::Sel::register(sel_name);
		::runtime::objc_msgSend($obj.as_ptr(), sel $(,$arg)+)
	});
)

#[macro_export]
macro_rules! object_struct(
	($name:ident<$($t:ident),+>) => (
		object_struct!($name, $($t),+)
	);
	($name:ident $(,$t:ident)*) => (
		pub struct $name<$($t),*> {
			nocopy: ::std::kinds::marker::NoCopy,
		}

		impl<$($t),*> ::runtime::Message for $name<$($t),*> { }

		impl<$($t),*> ::foundation::INSObject for $name<$($t),*> {
			fn class_name() -> ::ClassName<$name<$($t),*>> {
				::ClassName(stringify!($name))
			}
		}

		impl<$($t),*> ::std::cmp::PartialEq for $name<$($t),*> {
			fn eq(&self, other: &$name<$($t),*>) -> bool {
				use foundation::INSObject;
				self.is_equal(other)
			}
		}

		impl<$($t),*> ::std::cmp::Eq for $name<$($t),*> { }

		impl<$($t,)* S: ::std::hash::Writer> ::std::hash::Hash<S> for $name<$($t),*> {
			fn hash(&self, state: &mut S) {
				use foundation::INSObject;
				self.hash_code().hash(state);
			}
		}

		impl<$($t),*> ::std::fmt::Show for $name<$($t),*> {
			fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
				use foundation::{INSObject, INSString};
				self.description().as_str().fmt(f)
			}
		}
	);
)
<commit_msg>Began implementing add_method macro.<commit_after>#![macro_escape]

#[macro_export]
macro_rules! msg_send(
	($obj:expr $name:ident) => ({
		use runtime::ToMessage;
		let sel_name = stringify!($name);
		let sel = ::runtime::Sel::register(sel_name);
		::runtime::objc_msgSend($obj.as_ptr(), sel)
	});
	($obj:expr $($name:ident : $arg:expr)+) => ({
		use runtime::ToMessage;
		let sel_name = concat!($(stringify!($name), ':'),+);
		let sel = ::runtime::Sel::register(sel_name);
		::runtime::objc_msgSend($obj.as_ptr(), sel $(,$arg)+)
	});
)

#[macro_export]
macro_rules! object_struct(
	($name:ident<$($t:ident),+>) => (
		object_struct!($name, $($t),+)
	);
	($name:ident $(,$t:ident)*) => (
		pub struct $name<$($t),*> {
			nocopy: ::std::kinds::marker::NoCopy,
		}

		impl<$($t),*> ::runtime::Message for $name<$($t),*> { }

		impl<$($t),*> ::foundation::INSObject for $name<$($t),*> {
			fn class_name() -> ::ClassName<$name<$($t),*>> {
				::ClassName(stringify!($name))
			}
		}

		impl<$($t),*> ::std::cmp::PartialEq for $name<$($t),*> {
			fn eq(&self, other: &$name<$($t),*>) -> bool {
				use foundation::INSObject;
				self.is_equal(other)
			}
		}

		impl<$($t),*> ::std::cmp::Eq for $name<$($t),*> { }

		impl<$($t,)* S: ::std::hash::Writer> ::std::hash::Hash<S> for $name<$($t),*> {
			fn hash(&self, state: &mut S) {
				use foundation::INSObject;
				self.hash_code().hash(state);
			}
		}

		impl<$($t),*> ::std::fmt::Show for $name<$($t),*> {
			fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
				use foundation::{INSObject, INSString};
				self.description().as_str().fmt(f)
			}
		}
	);
)

#[macro_export]
macro_rules! add_method(
	(
		$cls:expr, ($self_ty:ty)$self_name:ident
		- ($ret_ty:ty) $name:ident : ($arg_ty:ty) $arg_name:ident
		$body:block
	) => ({
		extern fn _method($self_name: $self_ty, _cmd: ::runtime::Sel, $arg_name: $arg_ty) -> $ret_ty $body
		let sel = ::runtime::Sel::register(stringify!($name));
		let imp: ::runtime::Imp = unsafe { ::std::mem::transmute(_method) };
		let types = "v@:@";
		types.with_c_str(|types| unsafe {
			::runtime::class_addMethod($cls, sel, imp, types);
		})
	})
)
<|endoftext|>


<commit_before>#![allow(dead_code)]

use lexicon::Lexicon;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Orientation {
    Across,
    Down
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct WordspanAddress {
    start_row: usize,
    start_col: usize,
    orientation: Orientation,
    length: usize
}

impl WordspanAddress {
    pub fn new(start_row: usize, start_col: usize,
               orientation: Orientation, length: usize) -> Self {
        WordspanAddress {
            start_row: start_row,
            start_col: start_col,
            orientation: orientation,
            length: length
        }
    }

    pub fn oriented_new(start_this: usize, start_cross: usize,
                        orientation: Orientation, length: usize) -> Self {
        match orientation {
            Orientation::Across => WordspanAddress::new(
                start_this, start_cross, orientation, length),
            Orientation::Down => WordspanAddress::new(
                start_cross, start_this, orientation, length)
        }
    }
}

pub const BARRIER: char = '█';

#[derive(Clone, Debug)]
pub struct Puzzle {
    rows: usize,
    cols: usize,
    backing: Vec<char>
}

impl Puzzle {
    pub fn read(&self, row_index: usize, col_index: usize) -> char {
        self.backing[self.cols*row_index + col_index]
    }

    pub fn oriented_read(&self, orientation: Orientation,
                         this: usize, cross: usize) -> char {
        match orientation {
            Orientation::Across => self.read(this, cross),
            Orientation::Down => self.read(cross, this)
        }
    }

    pub fn write(&mut self, row_index: usize, col_index: usize,
                 letter: char) {
        self.backing[self.cols*row_index + col_index] = letter;
    }

    // XXX: returning a Vec<_> implies an allocation, but we can't return a
    // slice because the Down case isn't contiguous. Can we return a
    // Cow<[char]>? Profile and decide later
    pub fn read_wordspan(&self, address: WordspanAddress) -> Vec<char> {
        let start_square = self.cols*address.start_row + address.start_col;
        match address.orientation {
            Orientation::Across => {
                self.backing[start_square..start_square+address.length].to_vec()
            },
            Orientation::Down => {
                (0..address.length).map(|r| {
                    self.backing[start_square + self.cols*r]
                }).collect()
            }
        }
    }

    pub fn write_wordspan(&mut self, address: WordspanAddress, word: Vec<char>) {
        let start_square = self.cols*address.start_row + address.start_col;
        let mut offset = 0;
        for character in word {
            self.backing[start_square + offset] = character;
            offset += match address.orientation {
                Orientation::Across => 1,
                Orientation::Down => self.cols
            }
        }
    }

    pub fn oriented_wordspan_addresses(&self, orientation: Orientation)
                                       -> Vec<WordspanAddress> {
        let mut addresses = Vec::new();
        let (this_dimension, cross_dimension) = match orientation {
            Orientation::Across => (self.rows, self.cols),
            Orientation::Down => (self.cols, self.rows)
        };
        for beam in 0..this_dimension {
            let mut open_word_from: Option<usize> = None;
            for crossbeam in 0..cross_dimension {
                match open_word_from {
                    None => {
                        if self.oriented_read(orientation,
                                              beam, crossbeam) == BARRIER {
                            continue;
                        } else {
                            open_word_from = Some(crossbeam);
                        }
                    },
                    Some(open_from) => {
                        if self.oriented_read(orientation,
                                              beam, crossbeam) == BARRIER {
                            let address = WordspanAddress::oriented_new(
                                beam, open_from, orientation,
                                crossbeam-open_from
                            );
                            addresses.push(address);
                            open_word_from = None;
                        } else {
                            continue;
                        }
                    }
                }
            }
            // close off word closed by edge of puzzle rather than barrier
            if let Some(open_from) = open_word_from {
                let address = WordspanAddress::oriented_new(
                    beam, open_from, orientation,
                    cross_dimension-open_from
                );
                addresses.push(address);
            }
        }
        addresses
    }

    pub fn wordspan_addresses(&self) -> Vec<WordspanAddress> {
        let mut addresses = self.oriented_wordspan_addresses(Orientation::Across);
        addresses.extend(self.oriented_wordspan_addresses(Orientation::Down));
        addresses
    }

    pub fn is_solved(&self, lexicon: &Lexicon) -> bool {
        for address in self.wordspan_addresses() {
            let word = self.read_wordspan(address);
            if !lexicon.contains(&word) {
                return false;
            }
        }
        true
    }

}

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

    fn test_puzzle_i() -> Puzzle {
        Puzzle {
            rows: 5,
            cols: 5,
            backing: vec![
                'W', 'R', 'I', 'T', 'E',
                'A', 'A', 'R', 'O', 'N',
                'G', 'I', 'A', 'N', 'T',
                'E', 'S', 'T', 'E', 'R',
                'R', 'E', 'E', 'D', 'Y',
            ]
        }
    }

    fn test_puzzle_ii() -> Puzzle {
        Puzzle {
            rows: 7,
            cols: 11,
            backing: vec![
                'S', 'W', 'I', 'F', 'T', '█', 'S', 'T', 'A', 'C', 'K',
                'L', 'A', 'M', 'A', 'S', '█', 'L', 'A', 'M', 'A', 'R',
                'A', 'L', 'A', 'N', '█', 'L', 'E', 'G', 'A', 'T', 'O',
                'B', 'E', 'G', '█', 'L', 'A', 'D', '█', 'D', 'E', 'C',
                '█', 'S', 'E', 'L', 'I', 'M', '█', 'F', 'E', 'R', '█',
                '█', 'A', 'R', 'E', 'S', '█', 'P', 'I', 'U', 'S', '█',
                '█', '█', 'Y', 'I', 'P', '█', 'L', 'E', 'S', '█', '█',
            ]
        }
    }

    #[test]
    fn concerning_read_wordspan_across() {
        let write_address = WordspanAddress::new(0, 0, Orientation::Across, 5);
        assert_eq!(test_puzzle_i().read_wordspan(write_address),
                   charvec![WRITE]);
    }

    #[test]
    fn concerning_read_wordspan_down() {
        let raise_address = WordspanAddress::new(0, 1, Orientation::Down, 5);
        assert_eq!(test_puzzle_i().read_wordspan(raise_address),
                   charvec![RAISE]);
    }

    #[test]
    fn concerning_write_wordspan_across() {
        let mut puzzle = test_puzzle_i();
        // in more ways than one
        let write_address = WordspanAddress::new(0, 0, Orientation::Across, 5);
        puzzle.write_wordspan(write_address, charvec![PLATE]);
        assert_eq!(puzzle.read_wordspan(write_address),
                   charvec![PLATE]);
    }

    #[test]
    fn concerning_write_wordspan_down() {
        let mut puzzle = test_puzzle_i();
        let entry_address = WordspanAddress::new(0, 4, Orientation::Down, 5);
        puzzle.write_wordspan(entry_address, charvec![FAITH]);
        assert_eq!(puzzle.read_wordspan(entry_address),
                   charvec![FAITH]);
    }

    #[test]
    fn concerning_wordspan_addresses_across() {
        let puzzle = test_puzzle_ii();
        assert_eq!(
            &[WordspanAddress::new(0, 0, Orientation::Across, 5),
              WordspanAddress::new(0, 6, Orientation::Across, 5),
              WordspanAddress::new(1, 0, Orientation::Across, 5),
              WordspanAddress::new(1, 6, Orientation::Across, 5),
              WordspanAddress::new(2, 0, Orientation::Across, 4),
              WordspanAddress::new(2, 5, Orientation::Across, 6)],
            &puzzle.oriented_wordspan_addresses(Orientation::Across)[..6]
        );
    }

    #[test]
    fn concerning_wordspan_addresses_down() {
        let puzzle = test_puzzle_ii();
        assert_eq!(
            &[WordspanAddress::new(0, 0, Orientation::Down, 4),
              WordspanAddress::new(0, 1, Orientation::Down, 6),
              WordspanAddress::new(0, 2, Orientation::Down, 7),
              WordspanAddress::new(0, 3, Orientation::Down, 3),
              WordspanAddress::new(4, 3, Orientation::Down, 3),
              WordspanAddress::new(0, 4, Orientation::Down, 2)],
            &puzzle.oriented_wordspan_addresses(Orientation::Down)[..6]
        );
    }

    #[test]
    fn concerning_is_solved() {
        let lexicon = Lexicon::build(7);
        let puzzles = vec![test_puzzle_i(), test_puzzle_ii()];
        for mut puzzle in puzzles {
            assert!(puzzle.is_solved(&lexicon));
            puzzle.write(0, 0, 'Q');
            assert!(!puzzle.is_solved(&lexicon));
        }
    }

}
<commit_msg>gather cross requirements<commit_after>#![allow(dead_code)]

use std::collections::VecDeque;
use std::iter::FromIterator;

use lexicon::Lexicon;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Orientation {
    Across,
    Down
}

impl Orientation {
    pub fn reverse(&self) -> Self {
        match *self {
            Orientation::Across => Orientation::Down,
            Orientation::Down => Orientation::Across
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Locale {
    row: usize,
    col: usize
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct WordspanAddress {
    start_row: usize,
    start_col: usize,
    orientation: Orientation,
    length: usize
}

impl WordspanAddress {
    pub fn new(start_row: usize, start_col: usize,
               orientation: Orientation, length: usize) -> Self {
        WordspanAddress {
            start_row: start_row,
            start_col: start_col,
            orientation: orientation,
            length: length
        }
    }

    pub fn oriented_new(start_this: usize, start_cross: usize,
                        orientation: Orientation, length: usize) -> Self {
        match orientation {
            Orientation::Across => WordspanAddress::new(
                start_this, start_cross, orientation, length),
            Orientation::Down => WordspanAddress::new(
                start_cross, start_this, orientation, length)
        }
    }

    pub fn encompassed_locales(&self) -> Vec<Locale> {
        (0..self.length).map(|offset| {
            match self.orientation {
                Orientation::Across => Locale {
                    row: self.start_row,
                    col: self.start_col + offset
                },
                Orientation::Down => Locale {
                    row: self.start_row + offset,
                    col: self.start_col
                }
            }
        }).collect()
    }
}

pub const BARRIER: char = '█';

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CrossRequirement {
    length: usize,
    prefix: Vec<char>
}

impl CrossRequirement {
    pub fn new(length: usize, prefix: Vec<char>) -> Self {
        CrossRequirement {
            length: length,
            prefix: prefix,
        }
    }
}

#[derive(Clone, Debug)]
pub struct Puzzle {
    rows: usize,
    cols: usize,
    backing: Vec<char>
}

impl Puzzle {
    pub fn read(&self, row_index: usize, col_index: usize) -> char {
        self.backing[self.cols*row_index + col_index]
    }

    pub fn oriented_read(&self, orientation: Orientation,
                         this: usize, cross: usize) -> char {
        match orientation {
            Orientation::Across => self.read(this, cross),
            Orientation::Down => self.read(cross, this)
        }
    }

    pub fn write(&mut self, row_index: usize, col_index: usize,
                 letter: char) {
        self.backing[self.cols*row_index + col_index] = letter;
    }

    // XXX: returning a Vec<_> implies an allocation, but we can't return a
    // slice because the Down case isn't contiguous. Can we return a
    // Cow<[char]>? Profile and decide later
    pub fn read_wordspan(&self, address: WordspanAddress) -> Vec<char> {
        let start_square = self.cols*address.start_row + address.start_col;
        match address.orientation {
            Orientation::Across => {
                self.backing[start_square..start_square+address.length].to_vec()
            },
            Orientation::Down => {
                (0..address.length).map(|r| {
                    self.backing[start_square + self.cols*r]
                }).collect()
            }
        }
    }

    pub fn write_wordspan(&mut self, address: WordspanAddress, word: Vec<char>) {
        let start_square = self.cols*address.start_row + address.start_col;
        let mut offset = 0;
        for character in word {
            self.backing[start_square + offset] = character;
            offset += match address.orientation {
                Orientation::Across => 1,
                Orientation::Down => self.cols
            }
        }
    }

    pub fn oriented_wordspan_addresses(&self, orientation: Orientation)
                                       -> Vec<WordspanAddress> {
        let mut addresses = Vec::new();
        let (this_dimension, cross_dimension) = match orientation {
            Orientation::Across => (self.rows, self.cols),
            Orientation::Down => (self.cols, self.rows)
        };
        for beam in 0..this_dimension {
            let mut open_word_from: Option<usize> = None;
            for crossbeam in 0..cross_dimension {
                match open_word_from {
                    None => {
                        if self.oriented_read(orientation,
                                              beam, crossbeam) == BARRIER {
                            continue;
                        } else {
                            open_word_from = Some(crossbeam);
                        }
                    },
                    Some(open_from) => {
                        if self.oriented_read(orientation,
                                              beam, crossbeam) == BARRIER {
                            let address = WordspanAddress::oriented_new(
                                beam, open_from, orientation,
                                crossbeam-open_from
                            );
                            addresses.push(address);
                            open_word_from = None;
                        } else {
                            continue;
                        }
                    }
                }
            }
            // close off word closed by edge of puzzle rather than barrier
            if let Some(open_from) = open_word_from {
                let address = WordspanAddress::oriented_new(
                    beam, open_from, orientation,
                    cross_dimension-open_from
                );
                addresses.push(address);
            }
        }
        addresses
    }

    pub fn wordspan_addresses(&self) -> Vec<WordspanAddress> {
        let mut addresses = self.oriented_wordspan_addresses(Orientation::Across);
        addresses.extend(self.oriented_wordspan_addresses(Orientation::Down));
        addresses
    }

    pub fn is_solved(&self, lexicon: &Lexicon) -> bool {
        for address in self.wordspan_addresses() {
            let word = self.read_wordspan(address);
            if !lexicon.contains(&word) {
                return false;
            }
        }
        true
    }

    pub fn gather_cross_requirements(&self, address: WordspanAddress)
                                     -> Vec<CrossRequirement> {
        let mut cross_requirements = Vec::new();
        let mut our_locales = VecDeque::from_iter(
            address.encompassed_locales().into_iter());
        for cross_address in self.oriented_wordspan_addresses(
                address.orientation.reverse()) {
            if cross_address.encompassed_locales().contains(&our_locales[0]) {
                our_locales.pop_front();
                let requirement = CrossRequirement {
                    length: cross_address.length,
                    prefix: self.read_wordspan(cross_address).into_iter()
                        .take_while(|c| *c != ' ').collect()
                };
                cross_requirements.push(requirement);
            }
        }
        cross_requirements
    }

}

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

    fn test_puzzle_i() -> Puzzle {
        Puzzle {
            rows: 5,
            cols: 5,
            backing: vec![
                'W', 'R', 'I', 'T', 'E',
                'A', 'A', 'R', 'O', 'N',
                'G', 'I', 'A', 'N', 'T',
                'E', 'S', 'T', 'E', 'R',
                'R', 'E', 'E', 'D', 'Y',
            ]
        }
    }

    fn test_puzzle_ii() -> Puzzle {
        Puzzle {
            rows: 7,
            cols: 11,
            backing: vec![
                'S', 'W', 'I', 'F', 'T', '█', 'S', 'T', 'A', 'C', 'K',
                'L', 'A', 'M', 'A', 'S', '█', 'L', 'A', 'M', 'A', 'R',
                'A', 'L', 'A', 'N', '█', 'L', 'E', 'G', 'A', 'T', 'O',
                'B', 'E', 'G', '█', 'L', 'A', 'D', '█', 'D', 'E', 'C',
                '█', 'S', 'E', 'L', 'I', 'M', '█', 'F', 'E', 'R', '█',
                '█', 'A', 'R', 'E', 'S', '█', 'P', 'I', 'U', 'S', '█',
                '█', '█', 'Y', 'I', 'P', '█', 'L', 'E', 'S', '█', '█',
            ]
        }
    }

    fn test_puzzle_iii() -> Puzzle {
        Puzzle {
            rows: 5,
            cols: 4,
            backing: vec![
                'Q', 'U', 'I', 'Z',
                'U', 'N', 'D', 'O',
                'A', 'I', 'L', 'S',
                ' ', ' ', ' ', ' ', // ITEM
                ' ', ' ', ' ', ' ', // LEDA (in case you were wondering)
            ],
        }
    }

    #[test]
    fn concerning_read_wordspan_across() {
        let write_address = WordspanAddress::new(0, 0, Orientation::Across, 5);
        assert_eq!(test_puzzle_i().read_wordspan(write_address),
                   charvec![WRITE]);
    }

    #[test]
    fn concerning_read_wordspan_down() {
        let raise_address = WordspanAddress::new(0, 1, Orientation::Down, 5);
        assert_eq!(test_puzzle_i().read_wordspan(raise_address),
                   charvec![RAISE]);
    }

    #[test]
    fn concerning_write_wordspan_across() {
        let mut puzzle = test_puzzle_i();
        // in more ways than one
        let write_address = WordspanAddress::new(0, 0, Orientation::Across, 5);
        puzzle.write_wordspan(write_address, charvec![PLATE]);
        assert_eq!(puzzle.read_wordspan(write_address),
                   charvec![PLATE]);
    }

    #[test]
    fn concerning_write_wordspan_down() {
        let mut puzzle = test_puzzle_i();
        let entry_address = WordspanAddress::new(0, 4, Orientation::Down, 5);
        puzzle.write_wordspan(entry_address, charvec![FAITH]);
        assert_eq!(puzzle.read_wordspan(entry_address),
                   charvec![FAITH]);
    }

    #[test]
    fn concerning_wordspan_addresses_across() {
        let puzzle = test_puzzle_ii();
        assert_eq!(
            &[WordspanAddress::new(0, 0, Orientation::Across, 5),
              WordspanAddress::new(0, 6, Orientation::Across, 5),
              WordspanAddress::new(1, 0, Orientation::Across, 5),
              WordspanAddress::new(1, 6, Orientation::Across, 5),
              WordspanAddress::new(2, 0, Orientation::Across, 4),
              WordspanAddress::new(2, 5, Orientation::Across, 6)],
            &puzzle.oriented_wordspan_addresses(Orientation::Across)[..6]
        );
    }

    #[test]
    fn concerning_wordspan_addresses_down() {
        let puzzle = test_puzzle_ii();
        assert_eq!(
            &[WordspanAddress::new(0, 0, Orientation::Down, 4),
              WordspanAddress::new(0, 1, Orientation::Down, 6),
              WordspanAddress::new(0, 2, Orientation::Down, 7),
              WordspanAddress::new(0, 3, Orientation::Down, 3),
              WordspanAddress::new(4, 3, Orientation::Down, 3),
              WordspanAddress::new(0, 4, Orientation::Down, 2)],
            &puzzle.oriented_wordspan_addresses(Orientation::Down)[..6]
        );
    }

    #[test]
    fn concerning_is_solved() {
        let lexicon = Lexicon::build(7);
        let puzzles = vec![test_puzzle_i(), test_puzzle_ii()];
        for mut puzzle in puzzles {
            assert!(puzzle.is_solved(&lexicon));
            puzzle.write(0, 0, 'Q');
            assert!(!puzzle.is_solved(&lexicon));
        }
    }

    #[test]
    fn concerning_gathering_cross_requirements() {
        assert_eq!(
            vec![CrossRequirement::new(5, charvec![QUA]),
                 CrossRequirement::new(5, charvec![UNI]),
                 CrossRequirement::new(5, charvec![IDL]),
                 CrossRequirement::new(5, charvec![ZOS])],
            test_puzzle_iii()
                .gather_cross_requirements(
                    WordspanAddress::new(3, 0, Orientation::Across, 4))
        );
    }

}
<|endoftext|>


<commit_before>#![allow(dead_code)]

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Orientation {
    Across,
    Down
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct WordspanAddress {
    start_row: usize,
    start_col: usize,
    orientation: Orientation,
    length: usize
}

impl WordspanAddress {
    pub fn new(start_row: usize, start_col: usize,
               orientation: Orientation, length: usize) -> Self {
        WordspanAddress {
            start_row: start_row,
            start_col: start_col,
            orientation: orientation,
            length: length
        }
    }
}

pub const BARRIER: char = '█';

#[derive(Clone, Debug)]
pub struct Puzzle {
    rows: usize,
    cols: usize,
    backing: Vec<char>
}

impl Puzzle {
    pub fn read(&self, row_index: usize, col_index: usize) -> char {
        self.backing[self.cols*row_index + col_index]
    }

    pub fn write(&mut self, row_index: usize, col_index: usize,
             letter: char) {
        self.backing[self.cols*row_index + col_index] = letter;
    }

    // XXX: returning a Vec<_> implies an allocation, but we can't return a
    // slice because the Down case isn't contiguous. Can we return a
    // Cow<[char]>? Profile and decide later
    pub fn read_wordspan(&self, address: WordspanAddress) -> Vec<char> {
        let start_square = self.cols*address.start_row + address.start_col;
        match address.orientation {
            Orientation::Across => {
                self.backing[start_square..start_square+address.length].to_vec()
            },
            Orientation::Down => {
                (0..address.length).map(|r| {
                    self.backing[start_square + self.cols*r]
                }).collect()
            }
        }
    }

    pub fn write_wordspan(&mut self, address: WordspanAddress, word: Vec<char>) {
        let start_square = self.cols*address.start_row + address.start_col;
        let mut offset = 0;
        for character in word {
            self.backing[start_square + offset] = character;
            offset += match address.orientation {
                Orientation::Across => 1,
                Orientation::Down => self.cols
            }
        }
    }

    fn wordspan_addresses_across(&self) -> Vec<WordspanAddress> {
        let mut addresses = Vec::new();
        for row in 0..self.rows {
            let mut open_word_from: Option<usize> = None;
            for col in 0..self.cols {
                match open_word_from {
                    None => {
                        if self.read(row, col) == BARRIER {
                            continue;
                        } else {
                            open_word_from = Some(col);
                        }
                    },
                    Some(open_from) => {
                        if self.read(row, col) == BARRIER {
                            let address = WordspanAddress::new(
                                row, open_from, Orientation::Across,
                                col-open_from
                            );
                            addresses.push(address);
                            open_word_from = None;
                        } else {
                            continue;
                        }
                    }
                }
            }
            // close off word closed by edge of puzzle rather than barrier
            if let Some(open_from) = open_word_from {
                let address = WordspanAddress::new(
                    row, open_from, Orientation::Across,
                    self.cols-open_from
                );
                addresses.push(address);
            }
        }
        addresses
    }

}

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

    fn test_puzzle_i() -> Puzzle {
        Puzzle {
            rows: 5,
            cols: 5,
            backing: vec![
                'W', 'R', 'I', 'T', 'E',
                'A', 'A', 'R', 'O', 'N',
                'G', 'I', 'A', 'N', 'T',
                'E', 'S', 'T', 'E', 'R',
                'R', 'E', 'E', 'D', 'Y',
            ]
        }
    }

    fn test_puzzle_ii() -> Puzzle {
        Puzzle {
            rows: 7,
            cols: 11,
            backing: vec![
                'S', 'W', 'I', 'F', 'T', '█', 'S', 'T', 'A', 'C', 'K',
                'L', 'A', 'M', 'A', 'S', '█', 'L', 'A', 'M', 'A', 'R',
                'A', 'L', 'A', 'N', '█', 'L', 'E', 'G', 'A', 'T', 'O',
                'B', 'E', 'G', '█', 'L', 'A', 'D', '█', 'D', 'E', 'C',
                '█', 'S', 'E', 'L', 'I', 'M', '█', 'F', 'E', 'R', '█',
                '█', 'A', 'R', 'E', 'S', '█', 'P', 'I', 'U', 'S', '█',
                '█', '█', 'Y', 'I', 'P', '█', 'L', 'E', 'S', '█', '█',
            ]
        }
    }

    #[test]
    fn concerning_read_wordspan_across() {
        let write_address = WordspanAddress::new(0, 0, Orientation::Across, 5);
        assert_eq!(test_puzzle_i().read_wordspan(write_address),
                   charvec![WRITE]);
    }

    #[test]
    fn concerning_read_wordspan_down() {
        let raise_address = WordspanAddress::new(0, 1, Orientation::Down, 5);
        assert_eq!(test_puzzle_i().read_wordspan(raise_address),
                   charvec![RAISE]);
    }

    #[test]
    fn concerning_write_wordspan_across() {
        let mut puzzle = test_puzzle_i();
        // in more ways than one
        let write_address = WordspanAddress::new(0, 0, Orientation::Across, 5);
        puzzle.write_wordspan(write_address, charvec![PLATE]);
        assert_eq!(puzzle.read_wordspan(write_address),
                   charvec![PLATE]);
    }

    #[test]
    fn concerning_write_wordspan_down() {
        let mut puzzle = test_puzzle_i();
        let entry_address = WordspanAddress::new(0, 4, Orientation::Down, 5);
        puzzle.write_wordspan(entry_address, charvec![FAITH]);
        assert_eq!(puzzle.read_wordspan(entry_address),
                   charvec![FAITH]);
    }

    #[test]
    fn concerning_wordspan_addresses_across() {
        let puzzle = test_puzzle_ii();
        assert_eq!(
            &[WordspanAddress::new(0, 0, Orientation::Across, 5),
              WordspanAddress::new(0, 6, Orientation::Across, 5),
              WordspanAddress::new(1, 0, Orientation::Across, 5),
              WordspanAddress::new(1, 6, Orientation::Across, 5),
              WordspanAddress::new(2, 0, Orientation::Across, 4),
              WordspanAddress::new(2, 5, Orientation::Across, 6)],
            &puzzle.wordspan_addresses_across()[..6]
        );
    }
}
<commit_msg>generalized oriented wordspan addresses<commit_after>#![allow(dead_code)]

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Orientation {
    Across,
    Down
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct WordspanAddress {
    start_row: usize,
    start_col: usize,
    orientation: Orientation,
    length: usize
}

impl WordspanAddress {
    pub fn new(start_row: usize, start_col: usize,
               orientation: Orientation, length: usize) -> Self {
        WordspanAddress {
            start_row: start_row,
            start_col: start_col,
            orientation: orientation,
            length: length
        }
    }

    pub fn oriented_new(start_this: usize, start_cross: usize,
                        orientation: Orientation, length: usize) -> Self {
        match orientation {
            Orientation::Across => WordspanAddress::new(
                start_this, start_cross, orientation, length),
            Orientation::Down => WordspanAddress::new(
                start_cross, start_this, orientation, length)
        }
    }
}

pub const BARRIER: char = '█';

#[derive(Clone, Debug)]
pub struct Puzzle {
    rows: usize,
    cols: usize,
    backing: Vec<char>
}

impl Puzzle {
    pub fn read(&self, row_index: usize, col_index: usize) -> char {
        self.backing[self.cols*row_index + col_index]
    }

    pub fn oriented_read(&self, orientation: Orientation,
                         this: usize, cross: usize) -> char {
        match orientation {
            Orientation::Across => self.read(this, cross),
            Orientation::Down => self.read(cross, this)
        }
    }

    pub fn write(&mut self, row_index: usize, col_index: usize,
             letter: char) {
        self.backing[self.cols*row_index + col_index] = letter;
    }

    // XXX: returning a Vec<_> implies an allocation, but we can't return a
    // slice because the Down case isn't contiguous. Can we return a
    // Cow<[char]>? Profile and decide later
    pub fn read_wordspan(&self, address: WordspanAddress) -> Vec<char> {
        let start_square = self.cols*address.start_row + address.start_col;
        match address.orientation {
            Orientation::Across => {
                self.backing[start_square..start_square+address.length].to_vec()
            },
            Orientation::Down => {
                (0..address.length).map(|r| {
                    self.backing[start_square + self.cols*r]
                }).collect()
            }
        }
    }

    pub fn write_wordspan(&mut self, address: WordspanAddress, word: Vec<char>) {
        let start_square = self.cols*address.start_row + address.start_col;
        let mut offset = 0;
        for character in word {
            self.backing[start_square + offset] = character;
            offset += match address.orientation {
                Orientation::Across => 1,
                Orientation::Down => self.cols
            }
        }
    }

    pub fn oriented_wordspan_addresses(&self, orientation: Orientation)
                                       -> Vec<WordspanAddress> {
        let mut addresses = Vec::new();
        let (this_dimension, cross_dimension) = match orientation {
            Orientation::Across => (self.rows, self.cols),
            Orientation::Down => (self.cols, self.rows)
        };
        for beam in 0..this_dimension {
            let mut open_word_from: Option<usize> = None;
            for crossbeam in 0..cross_dimension {
                match open_word_from {
                    None => {
                        if self.oriented_read(orientation,
                                              beam, crossbeam) == BARRIER {
                            continue;
                        } else {
                            open_word_from = Some(crossbeam);
                        }
                    },
                    Some(open_from) => {
                        if self.oriented_read(orientation,
                                              beam, crossbeam) == BARRIER {
                            let address = WordspanAddress::oriented_new(
                                beam, open_from, orientation,
                                crossbeam-open_from
                            );
                            addresses.push(address);
                            open_word_from = None;
                        } else {
                            continue;
                        }
                    }
                }
            }
            // close off word closed by edge of puzzle rather than barrier
            if let Some(open_from) = open_word_from {
                let address = WordspanAddress::oriented_new(
                    beam, open_from, orientation,
                    cross_dimension-open_from
                );
                addresses.push(address);
            }
        }
        addresses
    }

    pub fn wordspan_addresses(&self) -> Vec<WordspanAddress> {
        let mut addresses = self.oriented_wordspan_addresses(Orientation::Across);
        addresses.extend(self.oriented_wordspan_addresses(Orientation::Down));
        addresses
    }

}

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

    fn test_puzzle_i() -> Puzzle {
        Puzzle {
            rows: 5,
            cols: 5,
            backing: vec![
                'W', 'R', 'I', 'T', 'E',
                'A', 'A', 'R', 'O', 'N',
                'G', 'I', 'A', 'N', 'T',
                'E', 'S', 'T', 'E', 'R',
                'R', 'E', 'E', 'D', 'Y',
            ]
        }
    }

    fn test_puzzle_ii() -> Puzzle {
        Puzzle {
            rows: 7,
            cols: 11,
            backing: vec![
                'S', 'W', 'I', 'F', 'T', '█', 'S', 'T', 'A', 'C', 'K',
                'L', 'A', 'M', 'A', 'S', '█', 'L', 'A', 'M', 'A', 'R',
                'A', 'L', 'A', 'N', '█', 'L', 'E', 'G', 'A', 'T', 'O',
                'B', 'E', 'G', '█', 'L', 'A', 'D', '█', 'D', 'E', 'C',
                '█', 'S', 'E', 'L', 'I', 'M', '█', 'F', 'E', 'R', '█',
                '█', 'A', 'R', 'E', 'S', '█', 'P', 'I', 'U', 'S', '█',
                '█', '█', 'Y', 'I', 'P', '█', 'L', 'E', 'S', '█', '█',
            ]
        }
    }

    #[test]
    fn concerning_read_wordspan_across() {
        let write_address = WordspanAddress::new(0, 0, Orientation::Across, 5);
        assert_eq!(test_puzzle_i().read_wordspan(write_address),
                   charvec![WRITE]);
    }

    #[test]
    fn concerning_read_wordspan_down() {
        let raise_address = WordspanAddress::new(0, 1, Orientation::Down, 5);
        assert_eq!(test_puzzle_i().read_wordspan(raise_address),
                   charvec![RAISE]);
    }

    #[test]
    fn concerning_write_wordspan_across() {
        let mut puzzle = test_puzzle_i();
        // in more ways than one
        let write_address = WordspanAddress::new(0, 0, Orientation::Across, 5);
        puzzle.write_wordspan(write_address, charvec![PLATE]);
        assert_eq!(puzzle.read_wordspan(write_address),
                   charvec![PLATE]);
    }

    #[test]
    fn concerning_write_wordspan_down() {
        let mut puzzle = test_puzzle_i();
        let entry_address = WordspanAddress::new(0, 4, Orientation::Down, 5);
        puzzle.write_wordspan(entry_address, charvec![FAITH]);
        assert_eq!(puzzle.read_wordspan(entry_address),
                   charvec![FAITH]);
    }

    #[test]
    fn concerning_wordspan_addresses_across() {
        let puzzle = test_puzzle_ii();
        assert_eq!(
            &[WordspanAddress::new(0, 0, Orientation::Across, 5),
              WordspanAddress::new(0, 6, Orientation::Across, 5),
              WordspanAddress::new(1, 0, Orientation::Across, 5),
              WordspanAddress::new(1, 6, Orientation::Across, 5),
              WordspanAddress::new(2, 0, Orientation::Across, 4),
              WordspanAddress::new(2, 5, Orientation::Across, 6)],
            &puzzle.oriented_wordspan_addresses(Orientation::Across)[..6]
        );
    }

    #[test]
    fn concerning_wordspan_addresses_down() {
        let puzzle = test_puzzle_ii();
        assert_eq!(
            &[WordspanAddress::new(0, 0, Orientation::Down, 4),
              WordspanAddress::new(0, 1, Orientation::Down, 6),
              WordspanAddress::new(0, 2, Orientation::Down, 7),
              WordspanAddress::new(0, 3, Orientation::Down, 3),
              WordspanAddress::new(4, 3, Orientation::Down, 3),
              WordspanAddress::new(0, 4, Orientation::Down, 2)],
            &puzzle.oriented_wordspan_addresses(Orientation::Down)[..6]
        );
    }
}
<|endoftext|>