<commit_before>use futures_core::future::Future;
use futures_core::task::{LocalWaker, Poll, Wake, Waker};
use slab::Slab;
use std::fmt;
use std::cell::UnsafeCell;
use std::marker::Unpin;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::task::local_waker_from_nonlocal;

/// A future that is cloneable and can be polled in multiple threads.
/// Use `Future::shared()` method to convert any future into a `Shared` future.
#[must_use = "futures do nothing unless polled"]
pub struct Shared<Fut: Future> {
    inner: Option<Arc<Inner<Fut>>>,
    waker_key: usize,
}

struct Inner<Fut: Future> {
    future_or_output: UnsafeCell<FutureOrOutput<Fut>>,
    notifier: Arc<Notifier>,
}

struct Notifier {
    state: AtomicUsize,
    wakers: Mutex<Option<Slab<Option<Waker>>>>,
}

// The future itself is polled behind the `Arc`, so it won't be moved
// when `Shared` is moved.
impl<Fut: Future> Unpin for Shared<Fut> {}

impl<Fut: Future> fmt::Debug for Shared<Fut> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Shared")
            .field("inner", &self.inner)
            .field("waker_key", &self.waker_key)
            .finish()
    }
}

impl<Fut: Future> fmt::Debug for Inner<Fut> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Inner")
            .finish()
    }
}

enum FutureOrOutput<Fut: Future> {
    Future(Fut),
    Output(Fut::Output),
}

unsafe impl<Fut> Send for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{}

unsafe impl<Fut> Sync for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{}

const IDLE: usize = 0;
const POLLING: usize = 1;
const REPOLL: usize = 2;
const COMPLETE: usize = 3;
const POISONED: usize = 4;

const NULL_WAKER_KEY: usize = usize::max_value();

impl<Fut: Future> Shared<Fut> {
    pub(super) fn new(future: Fut) -> Shared<Fut> {
        Shared {
            inner: Some(Arc::new(Inner {
                future_or_output: UnsafeCell::new(FutureOrOutput::Future(future)),
                notifier: Arc::new(Notifier {
                    state: AtomicUsize::new(IDLE),
                    wakers: Mutex::new(Some(Slab::new())),
                }),
            })),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Shared<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// If any clone of this `Shared` has completed execution and there are still at least one
    /// clone of this `Shared` alive then, returns its result immediately
    /// without blocking. Otherwise, returns None without triggering the work represented by
    /// this `Shared`.
    pub fn peek(&self) -> Option<Fut::Output> {
        match self.inner.as_ref().map(|inner| inner.notifier.state.load(SeqCst)) {
            Some(COMPLETE) => unsafe { Some(self.inner().clone_output()) },
            Some(POISONED) => panic!("inner future panicked during poll"),
            _ => None,
        }
    }

    fn inner(&self) -> &Arc<Inner<Fut>> {
        Self::inner_(&self.inner)
    }

    fn inner_(inner: &Option<Arc<Inner<Fut>>>) -> &Arc<Inner<Fut>> {
        inner.as_ref().expect("Shared future polled again after completion")
    }

    /// Registers the current task to receive a wakeup when `Inner` is awoken.
    fn set_waker(&mut self, lw: &LocalWaker) {
        // Acquire the lock first before checking COMPLETE to ensure there
        // isn't a race.
        let mut wakers = Self::inner_(&self.inner).notifier.wakers.lock().unwrap();
        let wakers = if let Some(wakers) = wakers.as_mut() {
            wakers
        } else {
            // The value is already available, so there's no need to set the waker.
            return
        };
        if self.waker_key == NULL_WAKER_KEY {
            self.waker_key = wakers.insert(Some(lw.clone().into_waker()));
        } else {
            let waker_slot = &mut wakers[self.waker_key];
            let needs_replacement = if let Some(old_waker) = waker_slot {
                // If there's still an unwoken waker in the slot, only replace
                // if the current one wouldn't wake the same task.
                !lw.will_wake_nonlocal(old_waker)
            } else {
                true
            };
            if needs_replacement {
                *waker_slot = Some(lw.clone().into_waker());
            }
        }
        debug_assert!(self.waker_key != NULL_WAKER_KEY);
    }

    /// Safety: callers must first ensure that `self.inner.state`
    /// is `COMPLETE`
    unsafe fn take_or_clone_output(inner: Arc<Inner<Fut>>) -> Fut::Output {
        match Arc::try_unwrap(inner) {
            Ok(inner) => {
                match inner.future_or_output.into_inner() {
                    FutureOrOutput::Output(item) => item,
                    FutureOrOutput::Future(_) => unreachable!(),
                }
            }
            Err(inner) => inner.clone_output(),
        }
    }

}

impl<Fut> Inner<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// Safety: callers must first ensure that `self.inner.state`
    /// is `COMPLETE`
    unsafe fn clone_output(&self) -> Fut::Output {
        match &*self.future_or_output.get() {
            FutureOrOutput::Output(item) => item.clone(),
            FutureOrOutput::Future(_) => unreachable!(),
        }
    }
}

impl<Fut: Future> Future for Shared<Fut>
where
    Fut::Output: Clone,
{
    type Output = Fut::Output;

    fn poll(mut self: Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
        let this = &mut *self;

        // Assert that we aren't completed
        this.inner();

        this.set_waker(lw);

        match this.inner().notifier.state.compare_and_swap(IDLE, POLLING, SeqCst) {
            IDLE => {
                // Lock acquired, fall through
            }
            POLLING | REPOLL => {
                // Another task is currently polling, at this point we just want
                // to ensure that our task handle is currently registered

                return Poll::Pending;
            }
            COMPLETE => {
                let inner = self.inner.take().unwrap();
                return unsafe { Poll::Ready(Self::take_or_clone_output(inner)) };
            }
            POISONED => panic!("inner future panicked during poll"),
            _ => unreachable!(),
        }

        let waker = local_waker_from_nonlocal(this.inner().notifier.clone());
        let lw = &waker;

        struct Reset<'a>(&'a AtomicUsize);

        impl<'a> Drop for Reset<'a> {
            fn drop(&mut self) {
                use std::thread;

                if thread::panicking() {
                    self.0.store(POISONED, SeqCst);
                }
            }
        }

        let output = loop {
            let inner = this.inner();
            let _reset = Reset(&inner.notifier.state);

            // Poll the future
            let res = unsafe {
                if let FutureOrOutput::Future(future) =
                    &mut *inner.future_or_output.get()
                {
                    Pin::new_unchecked(future).poll(lw)
                } else {
                    unreachable!()
                }
            };
            match res {
                Poll::Pending => {
                    // Not ready, try to release the handle
                    match inner
                        .notifier
                        .state
                        .compare_and_swap(POLLING, IDLE, SeqCst)
                    {
                        POLLING => {
                            // Success
                            return Poll::Pending;
                        }
                        REPOLL => {
                            // Gotta poll again!
                            let prev = inner.notifier.state.swap(POLLING, SeqCst);
                            assert_eq!(prev, REPOLL);
                        }
                        _ => unreachable!(),
                    }
                }
                Poll::Ready(output) => break output,
            }
        };

        if Arc::get_mut(this.inner.as_mut().unwrap()).is_some() {
            this.inner.take();
            return Poll::Ready(output);
        }

        let inner = this.inner();

        let _reset = Reset(&inner.notifier.state);

        unsafe {
            *inner.future_or_output.get() =
                FutureOrOutput::Output(output.clone());
        }

        // Complete the future
        let mut lock = inner.notifier.wakers.lock().unwrap();
        inner.notifier.state.store(COMPLETE, SeqCst);
        let wakers = &mut lock.take().unwrap();
        for (_key, opt_waker) in wakers {
            if let Some(waker) = opt_waker.take() {
                waker.wake();
            }
        }

        Poll::Ready(output)
    }
}

impl<Fut> Clone for Shared<Fut>
where
    Fut: Future,
{
    fn clone(&self) -> Self {
        Shared {
            inner: self.inner.clone(),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Drop for Shared<Fut>
where
    Fut: Future,
{
    fn drop(&mut self) {
        if self.waker_key != NULL_WAKER_KEY {
            if let Some(ref inner) = self.inner {
                if let Ok(mut wakers) = inner.notifier.wakers.lock() {
                    if let Some(wakers) = wakers.as_mut() {
                        wakers.remove(self.waker_key);
                    }
                }
            }
        }
    }
}

impl Wake for Notifier {
    fn wake(arc_self: &Arc<Self>) {
        arc_self.state.compare_and_swap(POLLING, REPOLL, SeqCst);

        let wakers = &mut *arc_self.wakers.lock().unwrap();
        if let Some(wakers) = wakers.as_mut() {
            for (_key, opt_waker) in wakers {
                if let Some(waker) = opt_waker.take() {
                    waker.wake();
                }
            }
        }
    }
}
<commit_msg>feat: Don't clone the output in Shared::peek<commit_after>use futures_core::future::Future;
use futures_core::task::{LocalWaker, Poll, Wake, Waker};
use slab::Slab;
use std::fmt;
use std::cell::UnsafeCell;
use std::marker::Unpin;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::task::local_waker_from_nonlocal;

/// A future that is cloneable and can be polled in multiple threads.
/// Use `Future::shared()` method to convert any future into a `Shared` future.
#[must_use = "futures do nothing unless polled"]
pub struct Shared<Fut: Future> {
    inner: Option<Arc<Inner<Fut>>>,
    waker_key: usize,
}

struct Inner<Fut: Future> {
    future_or_output: UnsafeCell<FutureOrOutput<Fut>>,
    notifier: Arc<Notifier>,
}

struct Notifier {
    state: AtomicUsize,
    wakers: Mutex<Option<Slab<Option<Waker>>>>,
}

// The future itself is polled behind the `Arc`, so it won't be moved
// when `Shared` is moved.
impl<Fut: Future> Unpin for Shared<Fut> {}

impl<Fut: Future> fmt::Debug for Shared<Fut> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Shared")
            .field("inner", &self.inner)
            .field("waker_key", &self.waker_key)
            .finish()
    }
}

impl<Fut: Future> fmt::Debug for Inner<Fut> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Inner")
            .finish()
    }
}

enum FutureOrOutput<Fut: Future> {
    Future(Fut),
    Output(Fut::Output),
}

unsafe impl<Fut> Send for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{}

unsafe impl<Fut> Sync for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{}

const IDLE: usize = 0;
const POLLING: usize = 1;
const REPOLL: usize = 2;
const COMPLETE: usize = 3;
const POISONED: usize = 4;

const NULL_WAKER_KEY: usize = usize::max_value();

impl<Fut: Future> Shared<Fut> {
    pub(super) fn new(future: Fut) -> Shared<Fut> {
        Shared {
            inner: Some(Arc::new(Inner {
                future_or_output: UnsafeCell::new(FutureOrOutput::Future(future)),
                notifier: Arc::new(Notifier {
                    state: AtomicUsize::new(IDLE),
                    wakers: Mutex::new(Some(Slab::new())),
                }),
            })),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Shared<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// If any clone of this `Shared` has completed execution and there are still at least one
    /// clone of this `Shared` alive then, returns its result immediately
    /// without blocking. Otherwise, returns None without triggering the work represented by
    /// this `Shared`.
    pub fn peek(&self) -> Option<&Fut::Output> {
        match self.inner.as_ref().map(|inner| inner.notifier.state.load(SeqCst)) {
            Some(COMPLETE) => unsafe { Some(self.inner().get_output()) },
            Some(POISONED) => panic!("inner future panicked during poll"),
            _ => None,
        }
    }

    fn inner(&self) -> &Arc<Inner<Fut>> {
        Self::inner_(&self.inner)
    }

    fn inner_(inner: &Option<Arc<Inner<Fut>>>) -> &Arc<Inner<Fut>> {
        inner.as_ref().expect("Shared future polled again after completion")
    }

    /// Registers the current task to receive a wakeup when `Inner` is awoken.
    fn set_waker(&mut self, lw: &LocalWaker) {
        // Acquire the lock first before checking COMPLETE to ensure there
        // isn't a race.
        let mut wakers = Self::inner_(&self.inner).notifier.wakers.lock().unwrap();
        let wakers = if let Some(wakers) = wakers.as_mut() {
            wakers
        } else {
            // The value is already available, so there's no need to set the waker.
            return
        };
        if self.waker_key == NULL_WAKER_KEY {
            self.waker_key = wakers.insert(Some(lw.clone().into_waker()));
        } else {
            let waker_slot = &mut wakers[self.waker_key];
            let needs_replacement = if let Some(old_waker) = waker_slot {
                // If there's still an unwoken waker in the slot, only replace
                // if the current one wouldn't wake the same task.
                !lw.will_wake_nonlocal(old_waker)
            } else {
                true
            };
            if needs_replacement {
                *waker_slot = Some(lw.clone().into_waker());
            }
        }
        debug_assert!(self.waker_key != NULL_WAKER_KEY);
    }

    /// Safety: callers must first ensure that `self.inner.state`
    /// is `COMPLETE`
    unsafe fn take_or_clone_output(inner: Arc<Inner<Fut>>) -> Fut::Output {
        match Arc::try_unwrap(inner) {
            Ok(inner) => {
                match inner.future_or_output.into_inner() {
                    FutureOrOutput::Output(item) => item,
                    FutureOrOutput::Future(_) => unreachable!(),
                }
            }
            Err(inner) => inner.get_output().clone(),
        }
    }

}

impl<Fut> Inner<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// Safety: callers must first ensure that `self.inner.state`
    /// is `COMPLETE`
    unsafe fn get_output(&self) -> &Fut::Output {
        match &*self.future_or_output.get() {
            FutureOrOutput::Output(ref item) => &item,
            FutureOrOutput::Future(_) => unreachable!(),
        }
    }
}

impl<Fut: Future> Future for Shared<Fut>
where
    Fut::Output: Clone,
{
    type Output = Fut::Output;

    fn poll(mut self: Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
        let this = &mut *self;

        // Assert that we aren't completed
        this.inner();

        this.set_waker(lw);

        match this.inner().notifier.state.compare_and_swap(IDLE, POLLING, SeqCst) {
            IDLE => {
                // Lock acquired, fall through
            }
            POLLING | REPOLL => {
                // Another task is currently polling, at this point we just want
                // to ensure that our task handle is currently registered

                return Poll::Pending;
            }
            COMPLETE => {
                let inner = self.inner.take().unwrap();
                return unsafe { Poll::Ready(Self::take_or_clone_output(inner)) };
            }
            POISONED => panic!("inner future panicked during poll"),
            _ => unreachable!(),
        }

        let waker = local_waker_from_nonlocal(this.inner().notifier.clone());
        let lw = &waker;

        struct Reset<'a>(&'a AtomicUsize);

        impl<'a> Drop for Reset<'a> {
            fn drop(&mut self) {
                use std::thread;

                if thread::panicking() {
                    self.0.store(POISONED, SeqCst);
                }
            }
        }

        let output = loop {
            let inner = this.inner();
            let _reset = Reset(&inner.notifier.state);

            // Poll the future
            let res = unsafe {
                if let FutureOrOutput::Future(future) =
                    &mut *inner.future_or_output.get()
                {
                    Pin::new_unchecked(future).poll(lw)
                } else {
                    unreachable!()
                }
            };
            match res {
                Poll::Pending => {
                    // Not ready, try to release the handle
                    match inner
                        .notifier
                        .state
                        .compare_and_swap(POLLING, IDLE, SeqCst)
                    {
                        POLLING => {
                            // Success
                            return Poll::Pending;
                        }
                        REPOLL => {
                            // Gotta poll again!
                            let prev = inner.notifier.state.swap(POLLING, SeqCst);
                            assert_eq!(prev, REPOLL);
                        }
                        _ => unreachable!(),
                    }
                }
                Poll::Ready(output) => break output,
            }
        };

        if Arc::get_mut(this.inner.as_mut().unwrap()).is_some() {
            this.inner.take();
            return Poll::Ready(output);
        }

        let inner = this.inner();

        let _reset = Reset(&inner.notifier.state);

        unsafe {
            *inner.future_or_output.get() =
                FutureOrOutput::Output(output.clone());
        }

        // Complete the future
        let mut lock = inner.notifier.wakers.lock().unwrap();
        inner.notifier.state.store(COMPLETE, SeqCst);
        let wakers = &mut lock.take().unwrap();
        for (_key, opt_waker) in wakers {
            if let Some(waker) = opt_waker.take() {
                waker.wake();
            }
        }

        Poll::Ready(output)
    }
}

impl<Fut> Clone for Shared<Fut>
where
    Fut: Future,
{
    fn clone(&self) -> Self {
        Shared {
            inner: self.inner.clone(),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Drop for Shared<Fut>
where
    Fut: Future,
{
    fn drop(&mut self) {
        if self.waker_key != NULL_WAKER_KEY {
            if let Some(ref inner) = self.inner {
                if let Ok(mut wakers) = inner.notifier.wakers.lock() {
                    if let Some(wakers) = wakers.as_mut() {
                        wakers.remove(self.waker_key);
                    }
                }
            }
        }
    }
}

impl Wake for Notifier {
    fn wake(arc_self: &Arc<Self>) {
        arc_self.state.compare_and_swap(POLLING, REPOLL, SeqCst);

        let wakers = &mut *arc_self.wakers.lock().unwrap();
        if let Some(wakers) = wakers.as_mut() {
            for (_key, opt_waker) in wakers {
                if let Some(waker) = opt_waker.take() {
                    waker.wake();
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use ast::Ast;
use ty::Ty;
use runtime::eval::{Value, BIF, eval};
use runtime::eval::Value::*;
use util::assoc::Assoc;
use name::*;
use std::rc::Rc;

use num::{BigInt};

#[derive(Debug,Clone,PartialEq)]
pub struct TypedValue {
    pub ty: Ast,
    pub val: Value
}

pub fn erase_type(tv: &TypedValue) -> Value { tv.val.clone() }
pub fn erase_value(tv: &TypedValue) -> Ty { Ty::new(tv.ty.clone()) }

pub fn core_typed_values() -> Assoc<Name, TypedValue> {
    let a_to_b = ast!({"type" "fn" : "param" => [(vr "a")], "ret" => (vr "b")});

    assoc_n!(
        "fix" =>
        tyf!( { "type" "forall_type" :
            "param" => ["a", "b"],
            "body" => { "type" "fn" :
                "param" => [ { "type" "fn" :
                    "param" => [(, a_to_b.clone())], "ret" => (, a_to_b.clone())} ],
                "ret" => (, a_to_b.clone()) }},
            // TODO: built-in functions, even though none of them work, shouldn't crash
            ( Function(cl) ) => {
                let new_env = cl.env.set(cl.params[0],
                    // reconstruct the invocation that caused this:
                    Function(Rc::new(::runtime::eval::Closure {
                        body: ast!({"expr" "apply" :
                            "rator" => (vr "fix"),
                            "rand" => [(vr "orig_arg")]}),
                        params: vec![],
                        env: assoc_n!("orig_arg" => Function(cl.clone()),
                                      // TODO: `core_values` does the `map` every time...
                                      "fix" => core_values().find_or_panic(&n("fix")).clone())})));
                eval(&cl.body, new_env).unwrap() // TODO: should be able to return `Result`
            }
        ),
        "plus" =>
        tf!([( "int", "int" ) -> "int"],
             ( Int(a), Int(b) ) => { Int( a.clone() + b ) }),
        "minus" =>
        tf!([( "int", "int" ) -> "int"],
             ( Int(a), Int(b) ) => { Int( a.clone() - b ) }),
        "times" =>
        tf!([( "int", "int" ) -> "int"],
             ( Int(a), Int(b) ) => { Int( a.clone() * b ) }),
        "zero?" =>
        tyf!( { "type" "fn" : "param" => [ {"type" "int" :}], "ret" => (vr "bool") },
              ( Int(a) ) => { val!(b   a == BigInt::from(0))}),
        "zero" => tf!( "int", val!(i 0) ),
        "one" => tf!( "int", val!(i 1) ),
        "two" => tf!( "int", val!(i 2) ),
        "three" => tf!( "int", val!(i 3) ),
        "four" => tf!( "int", val!(i 4) ),
        "five" => tf!( "int", val!(i 5) ),
        "six" => tf!( "int", val!(i 6) ),
        "seven" => tf!( "int", val!(i 7) ),
        "eight" => tf!( "int", val!(i 8) ),
        "nine" => tf!( "int", val!(i 9) ),
        "ten" => tf!( "int", val!(i 10) ),
        "false" => TypedValue { ty: ast!((vr "bool")), val: val!(b false)},
        "true" => TypedValue { ty: ast!((vr "bool")), val: val!(b true)}
    )
}

pub fn core_values() -> Assoc<Name, Value> {
    core_typed_values().map(&erase_type)
}

pub fn core_types() -> Assoc<Name, Ty> {
    core_typed_values().map(&erase_value).set(
        n("bool"), ty!(
            {"type" "enum" : "name" => [@"c" "True", "False"], "component" => [@"c" [], []]}))
}

#[test]
fn basic_core_value_evaluation() {
    use core_forms::find_core_form;

    let cte = core_typed_values();
    let ce = cte.map(&erase_type);

    assert_eq!(eval(
        &ast!({ find_core_form( "expr", "apply") ;
            "rator" => (vr "plus"),
            "rand" => [ (vr "one"), (vr "one") ]
        }),
        ce),
        Ok(Int(BigInt::from(2))));
}

#[test]
fn fixpoint_evaluation() {

    // Note that I'm cheating by leaving off the lambda imports because lambda evaluation
    //  doesn't take advantage of the betas in the first place.
    assert_eq!(eval(
        &ast!( {"expr" "apply" : "rator" =>
        { "expr" "apply" :
            "rator" => (vr "fix"),
            "rand" => [{ "expr" "lambda" :
                 "param" => [ "again" ],
                 "p_t" => [ /* TODO */ (vr "TODO")  ],
                 "body" => { "expr" "lambda" :
                     "param" => [ "n" ],
                     "p_t" => [ { "type" "int" : } ],
                     "body" => { "expr" "match" :
                         "scrutinee" => { "expr" "apply" : "rator" => (vr "zero?"),
                                                           "rand" => [(vr "n")] },
                         "p" => [@"c" {"pat" "enum_pat" : "component" => [], "name" => "True"  },
                                      {"pat" "enum_pat" : "component" => [], "name" => "False" }],
                         "arm" =>
                             [@"c"
                              (import ["p" = "scrutinee"] (vr "one")),
                              (import ["p" = "scrutinee"] { "expr" "apply" :
                                   "rator" => (vr "times"),
                                   "rand" =>
                                       [(vr "n"),
                                        { "expr" "apply" :
                                            "rator" => { "expr" "apply" :
                                                "rator" => (vr "again"), "rand" => []},
                                            "rand" => [{ "expr" "apply" :
                                                 "rator" => (vr "minus"),
                                                 "rand" => [(vr "n"), (vr "one")]}]}]})]}}}]},
        "rand" => [(vr "five")]}),
        core_values()),
        Ok(val!(i 120)));
}
<commit_msg>Fix a problem with `fix` (still doesn't typecheck, though).<commit_after>use ast::Ast;
use ty::Ty;
use runtime::eval::{Value, BIF, eval};
use runtime::eval::Value::*;
use util::assoc::Assoc;
use name::*;
use std::rc::Rc;

use num::{BigInt};

#[derive(Debug,Clone,PartialEq)]
pub struct TypedValue {
    pub ty: Ast,
    pub val: Value
}

pub fn erase_type(tv: &TypedValue) -> Value { tv.val.clone() }
pub fn erase_value(tv: &TypedValue) -> Ty { Ty::new(tv.ty.clone()) }

pub fn core_typed_values() -> Assoc<Name, TypedValue> {
    let a_to_b = ast!({"type" "fn" : "param" => [(vr "a")], "ret" => (vr "b")});
    let a_to_b_thunk = ast!({"type" "fn" : "param" => [], "ret" => (, a_to_b.clone() )});

    assoc_n!(
        "fix" =>
        tyf!( { "type" "forall_type" :
            "param" => ["a", "b"],
            "body" => (import [* [forall "param"]] { "type" "fn" :
                "param" => [ { "type" "fn" :
                    "param" => [(, a_to_b_thunk.clone())], "ret" => (, a_to_b.clone())} ],
                "ret" => (, a_to_b.clone()) })},
            // TODO: built-in functions, even though none of them work, shouldn't crash
            ( Function(cl) ) => {
                let new_env = cl.env.set(cl.params[0],
                    // reconstruct the invocation that caused this:
                    Function(Rc::new(::runtime::eval::Closure {
                        body: ast!({"expr" "apply" :
                            "rator" => (vr "fix"),
                            "rand" => [(vr "orig_arg")]}),
                        params: vec![],
                        env: assoc_n!("orig_arg" => Function(cl.clone()),
                                      // TODO: `core_values` does the `map` every time...
                                      "fix" => core_values().find_or_panic(&n("fix")).clone())})));
                eval(&cl.body, new_env).unwrap() // TODO: should be able to return `Result`
            }
        ),
        "plus" =>
        tf!([( "int", "int" ) -> "int"],
             ( Int(a), Int(b) ) => { Int( a.clone() + b ) }),
        "minus" =>
        tf!([( "int", "int" ) -> "int"],
             ( Int(a), Int(b) ) => { Int( a.clone() - b ) }),
        "times" =>
        tf!([( "int", "int" ) -> "int"],
             ( Int(a), Int(b) ) => { Int( a.clone() * b ) }),
        "zero?" =>
        tyf!( { "type" "fn" : "param" => [ {"type" "int" :}], "ret" => (vr "bool") },
              ( Int(a) ) => { val!(b   a == BigInt::from(0))}),
        "zero" => tf!( "int", val!(i 0) ),
        "one" => tf!( "int", val!(i 1) ),
        "two" => tf!( "int", val!(i 2) ),
        "three" => tf!( "int", val!(i 3) ),
        "four" => tf!( "int", val!(i 4) ),
        "five" => tf!( "int", val!(i 5) ),
        "six" => tf!( "int", val!(i 6) ),
        "seven" => tf!( "int", val!(i 7) ),
        "eight" => tf!( "int", val!(i 8) ),
        "nine" => tf!( "int", val!(i 9) ),
        "ten" => tf!( "int", val!(i 10) ),
        "false" => TypedValue { ty: ast!((vr "bool")), val: val!(b false)},
        "true" => TypedValue { ty: ast!((vr "bool")), val: val!(b true)}
    )
}

pub fn core_values() -> Assoc<Name, Value> {
    core_typed_values().map(&erase_type)
}

pub fn core_types() -> Assoc<Name, Ty> {
    core_typed_values().map(&erase_value).set(
        n("bool"), ty!(
            {"type" "enum" : "name" => [@"c" "True", "False"], "component" => [@"c" [], []]}))
}

#[test]
fn basic_core_value_evaluation() {
    use core_forms::find_core_form;

    let cte = core_typed_values();
    let ce = cte.map(&erase_type);

    assert_eq!(eval(
        &ast!({ find_core_form( "expr", "apply") ;
            "rator" => (vr "plus"),
            "rand" => [ (vr "one"), (vr "one") ]
        }),
        ce),
        Ok(Int(BigInt::from(2))));
}

#[test]
fn fixpoint_evaluation() {

    // Note that I'm cheating by leaving off the lambda imports because lambda evaluation
    //  doesn't take advantage of the betas in the first place.
    assert_eq!(eval(
        &ast!( {"expr" "apply" : "rator" =>
        { "expr" "apply" :
            "rator" => (vr "fix"),
            "rand" => [{ "expr" "lambda" :
                 "param" => [ "again" ],
                 "p_t" => [ /* TODO */ (vr "TODO")  ],
                 "body" => { "expr" "lambda" :
                     "param" => [ "n" ],
                     "p_t" => [ { "type" "int" : } ],
                     "body" => { "expr" "match" :
                         "scrutinee" => { "expr" "apply" : "rator" => (vr "zero?"),
                                                           "rand" => [(vr "n")] },
                         "p" => [@"c" {"pat" "enum_pat" : "component" => [], "name" => "True"  },
                                      {"pat" "enum_pat" : "component" => [], "name" => "False" }],
                         "arm" =>
                             [@"c"
                              (import ["p" = "scrutinee"] (vr "one")),
                              (import ["p" = "scrutinee"] { "expr" "apply" :
                                   "rator" => (vr "times"),
                                   "rand" =>
                                       [(vr "n"),
                                        { "expr" "apply" :
                                            "rator" => { "expr" "apply" :
                                                "rator" => (vr "again"), "rand" => []},
                                            "rand" => [{ "expr" "apply" :
                                                 "rator" => (vr "minus"),
                                                 "rand" => [(vr "n"), (vr "one")]}]}]})]}}}]},
        "rand" => [(vr "five")]}),
        core_values()),
        Ok(val!(i 120)));
}
<|endoftext|>


<commit_before>use colored::*;
use git2::Repository;
use std::env;

type MaybePrompt = Option<String>;
type Prompt = String;

fn main() {
    let path = env::var("PWD")
        .unwrap_or(String::new());
    print!("{} ", get_prompt(&path))
}

/// Returns the prompt with the git repository information at the given path
fn get_prompt(path: &str) -> Prompt {
    if let Some(i) = get_maybe_prompt(path) {
        format!("{}|{}{} {}",
                i.revision.white(),
                i.diff.white(),
                i.status,
                i.state)
    } else {
        String::new()
    }
}

#[cfg(test)]
mod get_prompt_tests {
    use super::get_prompt;
    use std::env;

    macro_rules! invalid_input_tests {
        ($($name:ident: $value:expr,)*) => {
        $(
            #[test]
            fn $name() {
                let (input, expected) = $value;
                assert_eq!(expected, get_prompt(input));
            }
        ) *
        }
    }

    invalid_input_tests! {
        empty_for_root: ("/", ""),
        empty_for_usr_local_bin: ("/usr/local/bin", ""),
        empty_for_non_existant_path: ("/tmp/i/do/not/exist", ""),
    }

    #[test]
    fn non_empty_for_pwd() {
        let path = env::var("PWD")
            .unwrap_or(String::new());
        assert_ne!(get_prompt(&path), "");
    }
}

fn get_maybe_prompt(path: &str) -> Option<PromptInfo> {
    let repo = Repository::discover(path).ok()?;

    Some(PromptInfo{
        revision: get_branch(&repo)
            .unwrap_or(get_sha(&repo)[..8].to_string()),
        diff: diff_count(&repo).unwrap_or(String::new()),
        status: status(&repo).unwrap_or(String::new()),
        state: state(&repo),
    })
}

struct PromptInfo {
    revision: String,
    diff: String,
    status: String,
    state: String,
}

fn get_branch(repo: &git2::Repository) -> MaybePrompt {
    let shorthand = repo.head()
        .ok()
        .as_ref()
        .and_then(|h| h.shorthand().map(String::from));

    if Some(String::from("HEAD")) == shorthand {
        None
    } else {
        shorthand
    }
}

fn get_sha(repo: &git2::Repository) -> Prompt {
    repo.head()
        .ok()
        .map(|h| h.target().map(|t| t.to_string()))
        .unwrap_or(None)
        .unwrap_or(String::new())
}

fn diff_count(repo: &git2::Repository) -> MaybePrompt {
    let rev = format!("origin/{}", get_branch(repo)?);
    Some(format!("{}{}",
        non_zero("↑", count_between(repo, &rev, "HEAD")),
        non_zero("↓", count_between(repo, "HEAD", &rev))))
}

fn count_between(repo: &git2::Repository, from: &str, to: &str) -> usize {
    if let Ok(mut revwalk) = repo.revwalk() {
        if revwalk.push_range(&format!("{}..{}", from, to)).is_ok() {
            return revwalk.count();
        }
    }
    0
}

fn status(repo: &git2::Repository) -> MaybePrompt {
    let mut opts = git2::StatusOptions::new();
    opts.include_untracked(false);
    opts.exclude_submodules(true);

    let statuses = repo.statuses(Some(&mut opts)).ok()?;
    let mut changes: usize = 0;
    let mut staged: usize = 0;
    let mut conflicts: usize = 0;
    for entry in statuses.iter().filter(|e| e.status() != git2::Status::CURRENT) {
        let s = entry.status();
        // NOTE: a single entry might belong to multiple categories
        if is_index_change(&s)    {staged += 1}
        if is_worktree_change(&s) {changes += 1}
        if s.is_conflicted()      {conflicts += 1}
    }

    if changes == 0 && staged == 0 && conflicts == 0 {
         Some(format!("{}", "✔".green()))
    } else {
        Some(format!("{}{}{}",
             non_zero("•", staged).green(),
             non_zero("✖", conflicts).red(),
             non_zero("+", changes).yellow()))
    }
}

fn is_index_change(s: &git2::Status) -> bool {
    s.is_index_deleted() ||
        s.is_index_modified() ||
        s.is_index_new() ||
        s.is_index_renamed() ||
        s.is_index_typechange()
}

fn is_worktree_change(s: &git2::Status) -> bool {
    s.is_wt_deleted() ||
        s.is_wt_modified() ||
        s.is_wt_new() ||
        s.is_wt_renamed() ||
        s.is_wt_typechange()
}

fn non_zero(prefix: &str, number: usize) -> String {
    if number == 0 {
        String::new()
    } else {
        format!("{}{}", prefix, number)
    }
}

// https://docs.rs/git2/0.7.5/git2/enum.RepositoryState.html
fn state(repo: &git2::Repository) -> Prompt {
    match repo.state() {
        git2::RepositoryState::Merge => format!("{}", "merge".red()),
        git2::RepositoryState::Rebase => format!("{}", "rebase".yellow()),
        git2::RepositoryState::RebaseInteractive => format!("{}", "rebase".yellow()),
        _ => String::new()
    }
}
<commit_msg>Start over<commit_after>use colored::*;
use std::env;

fn main() {
}
<|endoftext|>


<commit_before>use arc = std::arc;
use arc::ARC;
use au = gfx::geometry;
use font::{RunMetrics, Font};
use font_cache::FontCache;
use geom::point::Point2D;
use geom::size::Size2D;
use gfx::geometry::au;
use glyph::GlyphStore;
use layout::context::LayoutContext;
use libc::{c_void};
use servo_util::color;
use shaper::shape_textrun;
use std::arc;

pub struct TextRun {
    text: ~str,
    font: @Font,
    priv glyphs: GlyphStore,
}

// This is a hack until TextRuns are normally sendable, or
// we instead use ARC<TextRun> everywhere.
pub struct SendableTextRun {
    text: ~str,
    font_descriptor: (),
    priv glyphs: GlyphStore,
}

pub fn serialize(_cache: @FontCache, run: &TextRun) -> ~SendableTextRun {
    ~SendableTextRun {
        text: copy run.text,
        // TODO: actually serialize a font descriptor thingy
        font_descriptor: (),
        glyphs: copy run.glyphs,
    }
}

pub fn deserialize(cache: @FontCache, run: &SendableTextRun) -> @TextRun {
    @TextRun {
        text: copy run.text,
        // TODO: actually deserialize a font descriptor thingy
        font: cache.get_test_font(),
        glyphs: copy run.glyphs
    }
}

trait TextRunMethods {
    pure fn glyphs(&self) -> &self/GlyphStore;
    pure fn iter_indivisible_pieces_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool);
    // TODO: needs to take box style as argument, or move to TextBox.
    // see Gecko's IsTrimmableSpace methods for details.
    pure fn range_is_trimmable_whitespace(&self, offset: uint, length: uint) -> bool;

    fn metrics_for_range(offset: uint, length: uint) -> RunMetrics;
    fn min_width_for_range(offset: uint, length: uint) -> au;
    fn iter_natural_lines_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool);
}

impl TextRun : TextRunMethods {
    pure fn glyphs(&self) -> &self/GlyphStore { &self.glyphs }

    pure fn range_is_trimmable_whitespace(&self, offset: uint, length: uint) -> bool {
        let mut i = offset;
        while i < offset + length {
            let {ch, next} = str::char_range_at(self.text, i);
            match ch {
                ' ' | '\t' | '\r'  => {},
                _ => { return false; }
            }
            i = next;
        }
        return true;
    }

    fn metrics_for_range(offset: uint, length: uint) -> RunMetrics {
        self.font.measure_text(&self, offset, length)
    }

    fn min_width_for_range(offset: uint, length: uint) -> au {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        let mut max_piece_width = au(0);
        for self.iter_indivisible_pieces_for_range(offset, length) |piece_offset, piece_len| {
            let metrics = self.font.measure_text(&self, piece_offset, piece_len);
            if metrics.advance_width > max_piece_width {
                max_piece_width = metrics.advance_width;
            }
        };

        return max_piece_width;
    }

    fn iter_natural_lines_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool) {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        let mut clump_start = offset;
        let mut clump_end = offset;
        let mut in_clump = false;

        // clump non-linebreaks of nonzero length
        for uint::range(offset, offset + length) |i| {
            match (self.glyphs.char_is_newline(i), in_clump) {
                (false, true)  => { clump_end = i; }
                (false, false) => { in_clump = true; clump_start = i; clump_end = i; }
                (true, false) => { /* chomp whitespace */ }
                (true, true)  => {
                    in_clump = false;
                    // don't include the linebreak 'glyph'
                    // (we assume there's one GlyphEntry for a newline, and no actual glyphs)
                    if !f(clump_start, clump_end - clump_start + 1) { break }
                }
            }
        }

        // flush any remaining chars as a line
        if in_clump {
            clump_end = offset + length - 1;
            f(clump_start, clump_end - clump_start + 1);
        }
    }

    pure fn iter_indivisible_pieces_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool) {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        //TODO: need a more sophisticated model of words and possible breaks
        let text = str::view(self.text, offset, length);

        let mut clump_start = offset;

        loop {
            // clump contiguous non-whitespace
            match str::find_from(text, clump_start, |c| !char::is_whitespace(c)) {
                Some(clump_end) => {
                    if !f(clump_start, clump_end - clump_start + 1) { break }
                    clump_start = clump_end + 1;
                    // reached end
                    if clump_start == offset + length { break }
                },
                None => {
                    // nothing left, flush last piece containing only spaces
                    if clump_start < offset + length {
                        let clump_end = offset + length - 1;
                        f(clump_start, clump_end - clump_start + 1);
                    }
                    break
                }
            };

            // clump contiguous whitespace
            match str::find_from(text, clump_start, |c| char::is_whitespace(c)) {
                Some(clump_end) => {
                    if !f(clump_start, clump_end - clump_start + 1) { break }
                    clump_start = clump_end + 1;
                    // reached end
                    if clump_start == offset + length { break }
                }
                None => {
                    // nothing left, flush last piece containing only spaces
                    if clump_start < offset + length {
                        let clump_end = offset + length - 1;
                        f(clump_start, clump_end - clump_start + 1);
                    }
                    break
                }
            }
        }
    }
}

fn TextRun(font: @Font, text: ~str) -> TextRun {
    let glyph_store = GlyphStore(text.len());
    let run = TextRun {
        text: text,
        font: font,
        glyphs: glyph_store,
    };

    shape_textrun(&run);
    return run;
}

// this test can't run until LayoutContext is removed as an argument
// to min_width_for_range.
/*
#[test]
fn test_calc_min_break_width() {

    fn test_min_width_for_run(text: ~str, width: au) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, text);
        run.min_width_for_range(0, text.len())
    }

    test_min_width_for_run(~"firecracker", au::from_px(84));
    test_min_width_for_run(~"firecracker yumyum", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker yumyum", au::from_px(84));
}
*/

#[test]
#[ignore]
fn test_iter_indivisible_pieces() {
    fn test_pieces(text: ~str, res: ~[~str]) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, copy text);
        let mut slices : ~[~str] = ~[];
        for run.iter_indivisible_pieces_for_range(0, text.len()) |offset, length| {
            slices.push(str::slice(text, offset, length));
        }
        assert slices == res;
    }

    test_pieces(~"firecracker yumyum woopwoop", ~[~"firecracker", ~" ", ~"yumyum", ~" ", ~"woopwoop"]);
    test_pieces(~"firecracker yumyum     ", ~[~"firecracker", ~" ", ~"yumyum", ~"     "]);
    test_pieces(~"     firecracker yumyum", ~[~"     ", ~"firecracker", ~" ", ~"yumyum"]);
    test_pieces(~"  ", ~[~"  "]);
    test_pieces(~"", ~[]);
}

<commit_msg>Fix off-by-one errors in whitespace-word clumping.<commit_after>use arc = std::arc;
use arc::ARC;
use au = gfx::geometry;
use font::{RunMetrics, Font};
use font_cache::FontCache;
use geom::point::Point2D;
use geom::size::Size2D;
use gfx::geometry::au;
use glyph::GlyphStore;
use layout::context::LayoutContext;
use libc::{c_void};
use servo_util::color;
use shaper::shape_textrun;
use std::arc;

pub struct TextRun {
    text: ~str,
    font: @Font,
    priv glyphs: GlyphStore,
}

// This is a hack until TextRuns are normally sendable, or
// we instead use ARC<TextRun> everywhere.
pub struct SendableTextRun {
    text: ~str,
    font_descriptor: (),
    priv glyphs: GlyphStore,
}

pub fn serialize(_cache: @FontCache, run: &TextRun) -> ~SendableTextRun {
    ~SendableTextRun {
        text: copy run.text,
        // TODO: actually serialize a font descriptor thingy
        font_descriptor: (),
        glyphs: copy run.glyphs,
    }
}

pub fn deserialize(cache: @FontCache, run: &SendableTextRun) -> @TextRun {
    @TextRun {
        text: copy run.text,
        // TODO: actually deserialize a font descriptor thingy
        font: cache.get_test_font(),
        glyphs: copy run.glyphs
    }
}

trait TextRunMethods {
    pure fn glyphs(&self) -> &self/GlyphStore;
    pure fn iter_indivisible_pieces_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool);
    // TODO: needs to take box style as argument, or move to TextBox.
    // see Gecko's IsTrimmableSpace methods for details.
    pure fn range_is_trimmable_whitespace(&self, offset: uint, length: uint) -> bool;

    fn metrics_for_range(offset: uint, length: uint) -> RunMetrics;
    fn min_width_for_range(offset: uint, length: uint) -> au;
    fn iter_natural_lines_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool);
}

impl TextRun : TextRunMethods {
    pure fn glyphs(&self) -> &self/GlyphStore { &self.glyphs }

    pure fn range_is_trimmable_whitespace(&self, offset: uint, length: uint) -> bool {
        let mut i = offset;
        while i < offset + length {
            let {ch, next} = str::char_range_at(self.text, i);
            match ch {
                ' ' | '\t' | '\r'  => {},
                _ => { return false; }
            }
            i = next;
        }
        return true;
    }

    fn metrics_for_range(offset: uint, length: uint) -> RunMetrics {
        self.font.measure_text(&self, offset, length)
    }

    fn min_width_for_range(offset: uint, length: uint) -> au {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        let mut max_piece_width = au(0);
        for self.iter_indivisible_pieces_for_range(offset, length) |piece_offset, piece_len| {
            let metrics = self.font.measure_text(&self, piece_offset, piece_len);
            if metrics.advance_width > max_piece_width {
                max_piece_width = metrics.advance_width;
            }
        };

        return max_piece_width;
    }

    fn iter_natural_lines_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool) {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        let mut clump_start = offset;
        let mut clump_end = offset;
        let mut in_clump = false;

        // clump non-linebreaks of nonzero length
        for uint::range(offset, offset + length) |i| {
            match (self.glyphs.char_is_newline(i), in_clump) {
                (false, true)  => { clump_end = i; }
                (false, false) => { in_clump = true; clump_start = i; clump_end = i; }
                (true, false) => { /* chomp whitespace */ }
                (true, true)  => {
                    in_clump = false;
                    // don't include the linebreak 'glyph'
                    // (we assume there's one GlyphEntry for a newline, and no actual glyphs)
                    if !f(clump_start, clump_end - clump_start + 1) { break }
                }
            }
        }

        // flush any remaining chars as a line
        if in_clump {
            clump_end = offset + length - 1;
            f(clump_start, clump_end - clump_start + 1);
        }
    }

    pure fn iter_indivisible_pieces_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool) {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        //TODO: need a more sophisticated model of words and possible breaks
        let text = str::view(self.text, offset, length);

        let mut clump_start = offset;

        loop {
            // clump contiguous non-whitespace
            match str::find_from(text, clump_start, |c| !char::is_whitespace(c)) {
                Some(clump_end) => {
                    if !f(clump_start, clump_end - clump_start) { break }
                    clump_start = clump_end;
                    // reached end
                    if clump_start == offset + length { break }
                },
                None => {
                    // nothing left, flush last piece containing only whitespace
                    if clump_start < offset + length {
                        let clump_end = offset + length - 1;
                        f(clump_start, clump_end - clump_start + 1);
                    }
                    break
                }
            };

            // clump contiguous whitespace
            match str::find_from(text, clump_start, |c| char::is_whitespace(c)) {
                Some(clump_end) => {
                    if !f(clump_start, clump_end - clump_start) { break }
                    clump_start = clump_end;
                    // reached end
                    if clump_start == offset + length { break }
                }
                None => {
                    // nothing left, flush last piece containing only non-whitespaces
                    if clump_start < offset + length {
                        let clump_end = offset + length - 1;
                        f(clump_start, clump_end - clump_start + 1);
                    }
                    break
                }
            }
        }
    }
}

fn TextRun(font: @Font, text: ~str) -> TextRun {
    let glyph_store = GlyphStore(text.len());
    let run = TextRun {
        text: text,
        font: font,
        glyphs: glyph_store,
    };

    shape_textrun(&run);
    return run;
}

// this test can't run until LayoutContext is removed as an argument
// to min_width_for_range.
/*
#[test]
fn test_calc_min_break_width() {

    fn test_min_width_for_run(text: ~str, width: au) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, text);
        run.min_width_for_range(0, text.len())
    }

    test_min_width_for_run(~"firecracker", au::from_px(84));
    test_min_width_for_run(~"firecracker yumyum", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker yumyum", au::from_px(84));
}
*/

#[test]
#[ignore]
fn test_iter_indivisible_pieces() {
    fn test_pieces(text: ~str, res: ~[~str]) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, copy text);
        let mut slices : ~[~str] = ~[];
        for run.iter_indivisible_pieces_for_range(0, text.len()) |offset, length| {
            slices.push(str::slice(text, offset, length));
        }
        assert slices == res;
    }

    test_pieces(~"firecracker yumyum woopwoop", ~[~"firecracker", ~" ", ~"yumyum", ~" ", ~"woopwoop"]);
    test_pieces(~"firecracker yumyum     ", ~[~"firecracker", ~" ", ~"yumyum", ~"     "]);
    test_pieces(~"     firecracker yumyum", ~[~"     ", ~"firecracker", ~" ", ~"yumyum"]);
    test_pieces(~"  ", ~[~"  "]);
    test_pieces(~"", ~[]);
}

<|endoftext|>


<commit_before>use petgraph::{Graph, Undirected};
use rand;
use rand::distributions::{IndependentSample, Range};

pub type GridIndexType = u16;
pub type GridGraphNodeIndex = ::petgraph::graph::NodeIndex<GridIndexType>;

enum GridDirection {
    North,
    South,
    East,
    West,
}

#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone, Ord, PartialOrd)]
pub struct GridCoordinate {
    pub x: isize,
    pub y: isize,
}
impl GridCoordinate {
    pub fn new(x: isize, y: isize) -> GridCoordinate {
        GridCoordinate { x: x, y: y }
    }
}

pub struct SquareGrid {
    graph: Graph<(), (), Undirected, GridIndexType>,
    dimension_size: GridIndexType,
}
impl SquareGrid {
    pub fn new(dimension_size: GridIndexType) -> SquareGrid {

        let cells_count = dimension_size * dimension_size;
        let nodes_count_hint = cells_count as usize;
        let edges_count_hint = 4 * cells_count as usize - 4 * dimension_size as usize; // Probably overkill, but don't want any capacity panics

        let mut grid = SquareGrid {
            graph: Graph::with_capacity(nodes_count_hint, edges_count_hint),
            dimension_size: dimension_size,
        };
        for _ in 0..cells_count {
            let _ = grid.graph.add_node(());
        }

        grid
    }

    pub fn random_cell(&self) -> GridCoordinate {
        let range_end_exclusive = self.dimension_size as usize * self.dimension_size as usize;
        let random_pos = Range::new(0, range_end_exclusive);
        let mut rng = rand::thread_rng();
        let index = random_pos.ind_sample(&mut rng);
        index_to_grid_coordinate(self.dimension_size as usize, index)
    }

    /// Link two cells
    ///
    /// Todo - only allow links between adjacent cells? If `b` not in `g.neighbours(a)`.
    ///      - better to change the API to take an index and GridDirection
    ///
    /// Panics if a cell does not exist.
    pub fn link(&mut self, a: GridCoordinate, b: GridCoordinate) {
        if a != b {
            let a_index = self.grid_coordinate_graph_index(&a);
            let b_index = self.grid_coordinate_graph_index(&b);
            let _ = self.graph.update_edge(a_index, b_index, ());
        }
    }

    /// Unlink two cells, if a link exists between them.
    pub fn unlink(&mut self, a: GridCoordinate, b: GridCoordinate) {
        let a_index = self.grid_coordinate_graph_index(&a);
        let b_index = self.grid_coordinate_graph_index(&b);
        if let Some(edge_index) = self.graph.find_edge(a_index, b_index) {
            // This will invalidate the last edge index in the graph, which is fine as we
            // are not storing them for any reason.
            self.graph.remove_edge(edge_index);
        }
    }

    /// Cell nodes that are linked to a particular node by a passage.
    pub fn links(&self, coord: GridCoordinate) -> Vec<GridCoordinate> {
        self.graph
            .edges(self.grid_coordinate_graph_index(&coord))
            .map(|index_edge_data_pair| {
                let grid_node_index = index_edge_data_pair.0.clone();
                index_to_grid_coordinate(self.dimension_size as usize, grid_node_index.index())
            })
            .collect()
    }

    /// Cell nodes that are to the North, South, East or West of a particular node, but not
    /// necessarily linked by a passage.
    pub fn neighbours(&self, coord: GridCoordinate) -> Vec<GridCoordinate> {

        vec![offset_coordinate(&coord, GridDirection::North),
             offset_coordinate(&coord, GridDirection::South),
             offset_coordinate(&coord, GridDirection::East),
             offset_coordinate(&coord, GridDirection::West)]
            .into_iter()
            .filter(|adjacent_coord| self.is_valid_coordinate(adjacent_coord))
            .collect()
    }

    /// Are two cells in the grid linked?
    pub fn is_linked(&self, a: GridCoordinate, b: GridCoordinate) -> bool {
        let a_index = self.grid_coordinate_graph_index(&a);
        let b_index = self.grid_coordinate_graph_index(&b);
        self.graph.find_edge(a_index, b_index).is_some()
    }

    pub fn iter(&self) -> CellIter {
        let dim_size = self.dimension_size as usize;
        CellIter {
            current_cell_number: 0,
            dimension_size: dim_size,
            cells_count: dim_size * dim_size
        }
    }

    pub fn iter_row(&self) -> BatchIter {
        BatchIter {
            iter_type: BatchIterType::Row,
            current_index: 0,
            dimension_size: self.dimension_size as usize
        }
    }

    pub fn iter_column(&self) -> BatchIter {
        BatchIter {
            iter_type: BatchIterType::Column,
            current_index: 0,
            dimension_size: self.dimension_size as usize
        }
    }

    fn is_neighbour(&self, a: GridCoordinate, b: GridCoordinate) -> bool {
        self.neighbours(a).iter().any(|&coord| coord == b)
    }

    fn is_valid_coordinate(&self, coord: &GridCoordinate) -> bool {
        let (x, y) = (coord.x, coord.y);
        let dim_size = self.dimension_size as isize;
        if x < 0 || y < 0 || x >= dim_size || y >= dim_size {
            return false;
        }
        true
    }

    fn grid_coordinate_graph_index(&self, coord: &GridCoordinate) -> GridGraphNodeIndex {
        let grid_index_raw = ((coord.y * self.dimension_size as isize) + coord.x) as usize;
        GridGraphNodeIndex::new(grid_index_raw)
    }
}

pub struct CellIter {
    current_cell_number: usize,
    dimension_size: usize,
    cells_count: usize
}
impl Iterator for CellIter {
    type Item = GridCoordinate;
    fn next(&mut self) -> Option<Self::Item> {
        if self.current_cell_number < self.cells_count {
            let coord = index_to_grid_coordinate(self.dimension_size, self.current_cell_number);
            self.current_cell_number += 1;
            Some(coord)
        }
        else {
            None
        }
    }
}

enum BatchIterType {
    Row,
    Column
}
pub struct BatchIter {
    iter_type: BatchIterType,
    current_index: usize,
    dimension_size: usize,
}
impl Iterator for BatchIter {
    type Item = Vec<GridCoordinate>;
    fn next(&mut self) -> Option<Self::Item> {
        if self.current_index < self.dimension_size {
            let coords = (0..self.dimension_size)
                                 .into_iter()
                                 .map(|i| match self.iter_type {
                                    BatchIterType::Row    => GridCoordinate::new(i as isize, self.current_index as isize),
                                    BatchIterType::Column => GridCoordinate::new(self.current_index as isize, i as isize)
                                  })
                                 .collect();
            self.current_index += 1;
            Some(coords)
        } else {
            None
        }
    }
}

fn index_to_grid_coordinate(dimension_size: usize, one_dimensional_index: usize) -> GridCoordinate {
    let y = one_dimensional_index / dimension_size;
    let x = one_dimensional_index - (y * dimension_size);
    GridCoordinate {
        x: x as isize,
        y: y as isize,
    }
}

fn offset_coordinate(coord: &GridCoordinate, dir: GridDirection) -> GridCoordinate {
    let (x, y) = (coord.x, coord.y);
    match dir {
        GridDirection::North => GridCoordinate { x: x, y: y - 1 },
        GridDirection::South => GridCoordinate { x: x, y: y + 1 },
        GridDirection::East => GridCoordinate { x: x + 1, y: y },
        GridDirection::West => GridCoordinate { x: x - 1, y: y },
    }
}

#[cfg(test)]
mod test {

    use super::*;
    use itertools::Itertools; // a trait

    #[test]
    fn neighbour_cells() {
        let g = SquareGrid::new(10);

        let check_expected_neighbours = |coord, vec_expected_neighbours: Vec<GridCoordinate>| {
            let node_indices = g.neighbours(coord)
                                .into_iter()
                                .sorted();
            let expected_indices = vec_expected_neighbours.into_iter()
                                                          .sorted();
            assert_eq!(node_indices, expected_indices);
        };
        let gc = |x, y| GridCoordinate::new(x, y);

        // corners
        check_expected_neighbours(gc(0, 0), vec![gc(1, 0), gc(0, 1)]);
        check_expected_neighbours(gc(9, 0), vec![gc(8, 0), gc(9, 1)]);
        check_expected_neighbours(gc(0, 9), vec![gc(0, 8), gc(1, 9)]);
        check_expected_neighbours(gc(9, 9), vec![gc(9, 8), gc(8, 9)]);

        // side element examples
        check_expected_neighbours(gc(1, 0), vec![gc(0, 0), gc(1, 1), gc(2, 0)]);
        check_expected_neighbours(gc(0, 1), vec![gc(0, 0), gc(0, 2), gc(1, 1)]);
        check_expected_neighbours(gc(0, 8), vec![gc(1, 8), gc(0, 7), gc(0, 9)]);
        check_expected_neighbours(gc(9, 8), vec![gc(9, 7), gc(9, 9), gc(8, 8)]);

        // Some place with 4 neighbours inside the grid
        check_expected_neighbours(gc(1, 1), vec![gc(0, 1), gc(1, 0), gc(2, 1), gc(1, 2)]);
    }

    #[test]
    fn random_cell() {
        let g = SquareGrid::new(4);
        let cells_count = 4 * 4;
        for _ in 0..1000 {
            let coord = g.random_cell();
            assert!(coord.x >= 0 && coord.x < cells_count);
            assert!(coord.y >= 0 && coord.y < cells_count);
        }
    }

    #[test]
    fn linking_cells() {
        let mut g = SquareGrid::new(4);
        let a = GridCoordinate::new(0, 1);
        let b = GridCoordinate::new(0, 2);
        let c = GridCoordinate::new(0, 3);

        // I'd rather use a closure, but it needs to borrow the graph immutably until
        // it goes out of scope
        // Passing the graph in allows me to use a plain function:
        // let links_sorted = |n: GridGraphNodeIndex, g: &SquareGrid| -> Vec<GridGraphNodeIndex> {
        //     g.links(n).into_iter().sorted()
        // };
        // but it's uglier in that I need to explicitly pass in the SquareGrid ref all the time
        macro_rules! links_sorted {
            ($x:expr) => (g.links($x).into_iter().sorted())
        }
        // Testing that the order of the arguments to `is_linked` does not matter
        macro_rules! bi_check_linked {
            ($x:expr, $y:expr) => (g.is_linked($x, $y) && g.is_linked($y, $x))
        }

        // a, b and c start with no links
        assert!(!bi_check_linked!(a, b));
        assert!(!bi_check_linked!(a, c));
        assert!(!bi_check_linked!(b, c));
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![]);
        assert_eq!(links_sorted!(c), vec![]);

        g.link(a, b);
        // a - b linked bi-directionally
        assert!(bi_check_linked!(a, b));
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a]);

        g.link(b, c);
        // a - b still linked bi-directionally after linking b - c
        // b linked to a & c bi-directionally
        // c linked to b bi-directionally
        assert!(bi_check_linked!(a, b));
        assert!(bi_check_linked!(b, c));
        assert!(!bi_check_linked!(a, c));
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a, c]);
        assert_eq!(links_sorted!(c), vec![b]);

        // a - b still linked bi-directionally after updating exist link
        assert!(bi_check_linked!(a, b));
        assert!(bi_check_linked!(b, c));
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a, c]);

        // a - b unlinked
        // b still linked to c bi-directionally
        g.unlink(a, b);
        assert!(!bi_check_linked!(a, b));
        assert!(bi_check_linked!(b, c));
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![c]);
        assert_eq!(links_sorted!(c), vec![b]);

        // a, b and c start all unlinked again
        g.unlink(b, c);
        assert!(!bi_check_linked!(a, b));
        assert!(!bi_check_linked!(a, c));
        assert!(!bi_check_linked!(b, c));
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![]);
        assert_eq!(links_sorted!(c), vec![]);
    }

    #[test]
    fn no_self_linked_cycles() {
        let mut g = SquareGrid::new(4);
        let a = GridCoordinate::new(0, 0);
        g.link(a, a);
        assert_eq!(g.links(a), vec![]);
    }

    #[test]
    fn no_parallel_duplicated_linked_cells() {
        let mut g = SquareGrid::new(4);
        let a = GridCoordinate::new(0, 0);
        let b = GridCoordinate::new(0, 1);
        g.link(a, b);
        g.link(a, b);
        assert_eq!(g.links(a), vec![b]);
        assert_eq!(g.links(b), vec![a]);

        g.unlink(a, b);
        assert_eq!(g.links(a), vec![]);
        assert_eq!(g.links(b), vec![]);
    }
}
<commit_msg>SquareGrid iterator tests.<commit_after>use petgraph::{Graph, Undirected};
use rand;
use rand::distributions::{IndependentSample, Range};

pub type GridIndexType = u16;
pub type GridGraphNodeIndex = ::petgraph::graph::NodeIndex<GridIndexType>;

enum GridDirection {
    North,
    South,
    East,
    West,
}

#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone, Ord, PartialOrd)]
pub struct GridCoordinate {
    pub x: isize,
    pub y: isize,
}
impl GridCoordinate {
    pub fn new(x: isize, y: isize) -> GridCoordinate {
        GridCoordinate { x: x, y: y }
    }
}

pub struct SquareGrid {
    graph: Graph<(), (), Undirected, GridIndexType>,
    dimension_size: GridIndexType,
}
impl SquareGrid {
    pub fn new(dimension_size: GridIndexType) -> SquareGrid {

        let cells_count = dimension_size * dimension_size;
        let nodes_count_hint = cells_count as usize;
        let edges_count_hint = 4 * cells_count as usize - 4 * dimension_size as usize; // Probably overkill, but don't want any capacity panics

        let mut grid = SquareGrid {
            graph: Graph::with_capacity(nodes_count_hint, edges_count_hint),
            dimension_size: dimension_size,
        };
        for _ in 0..cells_count {
            let _ = grid.graph.add_node(());
        }

        grid
    }

    pub fn random_cell(&self) -> GridCoordinate {
        let range_end_exclusive = self.dimension_size as usize * self.dimension_size as usize;
        let random_pos = Range::new(0, range_end_exclusive);
        let mut rng = rand::thread_rng();
        let index = random_pos.ind_sample(&mut rng);
        index_to_grid_coordinate(self.dimension_size as usize, index)
    }

    /// Link two cells
    ///
    /// Todo - only allow links between adjacent cells? If `b` not in `g.neighbours(a)`.
    ///      - better to change the API to take an index and GridDirection
    ///
    /// Panics if a cell does not exist.
    pub fn link(&mut self, a: GridCoordinate, b: GridCoordinate) {
        if a != b {
            let a_index = self.grid_coordinate_graph_index(&a);
            let b_index = self.grid_coordinate_graph_index(&b);
            let _ = self.graph.update_edge(a_index, b_index, ());
        }
    }

    /// Unlink two cells, if a link exists between them.
    pub fn unlink(&mut self, a: GridCoordinate, b: GridCoordinate) {
        let a_index = self.grid_coordinate_graph_index(&a);
        let b_index = self.grid_coordinate_graph_index(&b);
        if let Some(edge_index) = self.graph.find_edge(a_index, b_index) {
            // This will invalidate the last edge index in the graph, which is fine as we
            // are not storing them for any reason.
            self.graph.remove_edge(edge_index);
        }
    }

    /// Cell nodes that are linked to a particular node by a passage.
    pub fn links(&self, coord: GridCoordinate) -> Vec<GridCoordinate> {
        self.graph
            .edges(self.grid_coordinate_graph_index(&coord))
            .map(|index_edge_data_pair| {
                let grid_node_index = index_edge_data_pair.0.clone();
                index_to_grid_coordinate(self.dimension_size as usize, grid_node_index.index())
            })
            .collect()
    }

    /// Cell nodes that are to the North, South, East or West of a particular node, but not
    /// necessarily linked by a passage.
    pub fn neighbours(&self, coord: GridCoordinate) -> Vec<GridCoordinate> {

        vec![offset_coordinate(&coord, GridDirection::North),
             offset_coordinate(&coord, GridDirection::South),
             offset_coordinate(&coord, GridDirection::East),
             offset_coordinate(&coord, GridDirection::West)]
            .into_iter()
            .filter(|adjacent_coord| self.is_valid_coordinate(adjacent_coord))
            .collect()
    }

    /// Are two cells in the grid linked?
    pub fn is_linked(&self, a: GridCoordinate, b: GridCoordinate) -> bool {
        let a_index = self.grid_coordinate_graph_index(&a);
        let b_index = self.grid_coordinate_graph_index(&b);
        self.graph.find_edge(a_index, b_index).is_some()
    }

    pub fn iter(&self) -> CellIter {
        let dim_size = self.dimension_size as usize;
        CellIter {
            current_cell_number: 0,
            dimension_size: dim_size,
            cells_count: dim_size * dim_size
        }
    }

    pub fn iter_row(&self) -> BatchIter {
        BatchIter {
            iter_type: BatchIterType::Row,
            current_index: 0,
            dimension_size: self.dimension_size as usize
        }
    }

    pub fn iter_column(&self) -> BatchIter {
        BatchIter {
            iter_type: BatchIterType::Column,
            current_index: 0,
            dimension_size: self.dimension_size as usize
        }
    }

    fn is_neighbour(&self, a: GridCoordinate, b: GridCoordinate) -> bool {
        self.neighbours(a).iter().any(|&coord| coord == b)
    }

    fn is_valid_coordinate(&self, coord: &GridCoordinate) -> bool {
        let (x, y) = (coord.x, coord.y);
        let dim_size = self.dimension_size as isize;
        if x < 0 || y < 0 || x >= dim_size || y >= dim_size {
            return false;
        }
        true
    }

    fn grid_coordinate_graph_index(&self, coord: &GridCoordinate) -> GridGraphNodeIndex {
        let grid_index_raw = ((coord.y * self.dimension_size as isize) + coord.x) as usize;
        GridGraphNodeIndex::new(grid_index_raw)
    }
}

pub struct CellIter {
    current_cell_number: usize,
    dimension_size: usize,
    cells_count: usize
}
impl Iterator for CellIter {
    type Item = GridCoordinate;
    fn next(&mut self) -> Option<Self::Item> {
        if self.current_cell_number < self.cells_count {
            let coord = index_to_grid_coordinate(self.dimension_size, self.current_cell_number);
            self.current_cell_number += 1;
            Some(coord)
        }
        else {
            None
        }
    }
}

enum BatchIterType {
    Row,
    Column
}
pub struct BatchIter {
    iter_type: BatchIterType,
    current_index: usize,
    dimension_size: usize,
}
impl Iterator for BatchIter {
    type Item = Vec<GridCoordinate>;
    fn next(&mut self) -> Option<Self::Item> {
        if self.current_index < self.dimension_size {
            let coords = (0..self.dimension_size)
                                 .into_iter()
                                 .map(|i| match self.iter_type {
                                    BatchIterType::Row    => GridCoordinate::new(i as isize, self.current_index as isize),
                                    BatchIterType::Column => GridCoordinate::new(self.current_index as isize, i as isize)
                                  })
                                 .collect();
            self.current_index += 1;
            Some(coords)
        } else {
            None
        }
    }
}

fn index_to_grid_coordinate(dimension_size: usize, one_dimensional_index: usize) -> GridCoordinate {
    let y = one_dimensional_index / dimension_size;
    let x = one_dimensional_index - (y * dimension_size);
    GridCoordinate {
        x: x as isize,
        y: y as isize,
    }
}

fn offset_coordinate(coord: &GridCoordinate, dir: GridDirection) -> GridCoordinate {
    let (x, y) = (coord.x, coord.y);
    match dir {
        GridDirection::North => GridCoordinate { x: x, y: y - 1 },
        GridDirection::South => GridCoordinate { x: x, y: y + 1 },
        GridDirection::East => GridCoordinate { x: x + 1, y: y },
        GridDirection::West => GridCoordinate { x: x - 1, y: y },
    }
}

#[cfg(test)]
mod test {

    use super::*;
    use itertools::Itertools; // a trait

    #[test]
    fn neighbour_cells() {
        let g = SquareGrid::new(10);

        let check_expected_neighbours = |coord, vec_expected_neighbours: Vec<GridCoordinate>| {
            let node_indices = g.neighbours(coord)
                                .into_iter()
                                .sorted();
            let expected_indices = vec_expected_neighbours.into_iter()
                                                          .sorted();
            assert_eq!(node_indices, expected_indices);
        };
        let gc = |x, y| GridCoordinate::new(x, y);

        // corners
        check_expected_neighbours(gc(0, 0), vec![gc(1, 0), gc(0, 1)]);
        check_expected_neighbours(gc(9, 0), vec![gc(8, 0), gc(9, 1)]);
        check_expected_neighbours(gc(0, 9), vec![gc(0, 8), gc(1, 9)]);
        check_expected_neighbours(gc(9, 9), vec![gc(9, 8), gc(8, 9)]);

        // side element examples
        check_expected_neighbours(gc(1, 0), vec![gc(0, 0), gc(1, 1), gc(2, 0)]);
        check_expected_neighbours(gc(0, 1), vec![gc(0, 0), gc(0, 2), gc(1, 1)]);
        check_expected_neighbours(gc(0, 8), vec![gc(1, 8), gc(0, 7), gc(0, 9)]);
        check_expected_neighbours(gc(9, 8), vec![gc(9, 7), gc(9, 9), gc(8, 8)]);

        // Some place with 4 neighbours inside the grid
        check_expected_neighbours(gc(1, 1), vec![gc(0, 1), gc(1, 0), gc(2, 1), gc(1, 2)]);
    }

    #[test]
    fn random_cell() {
        let g = SquareGrid::new(4);
        let cells_count = 4 * 4;
        for _ in 0..1000 {
            let coord = g.random_cell();
            assert!(coord.x >= 0 && coord.x < cells_count);
            assert!(coord.y >= 0 && coord.y < cells_count);
        }
    }

    #[test]
    fn cell_iter() {
        let g = SquareGrid::new(2);
        assert_eq!(g.iter().collect::<Vec<GridCoordinate>>(),
                   vec![GridCoordinate::new(0,0), GridCoordinate::new(1,0),
                        GridCoordinate::new(0,1), GridCoordinate::new(1,1)]);
    }

    #[test]
    fn row_iter() {
        let g = SquareGrid::new(2);
        assert_eq!(g.iter_row().collect::<Vec<Vec<GridCoordinate>>>(),
                   vec![vec![GridCoordinate::new(0,0), GridCoordinate::new(1,0)],
                        vec![GridCoordinate::new(0,1), GridCoordinate::new(1,1)]]);
    }

    #[test]
    fn column_iter() {
        let g = SquareGrid::new(2);
        assert_eq!(g.iter_column().collect::<Vec<Vec<GridCoordinate>>>(),
                   vec![vec![GridCoordinate::new(0,0), GridCoordinate::new(0,1)],
                        vec![GridCoordinate::new(1,0), GridCoordinate::new(1,1)]]);
    }

    #[test]
    fn linking_cells() {
        let mut g = SquareGrid::new(4);
        let a = GridCoordinate::new(0, 1);
        let b = GridCoordinate::new(0, 2);
        let c = GridCoordinate::new(0, 3);

        // I'd rather use a closure, but it needs to borrow the graph immutably until
        // it goes out of scope
        // Passing the graph in allows me to use a plain function:
        // let links_sorted = |n: GridGraphNodeIndex, g: &SquareGrid| -> Vec<GridGraphNodeIndex> {
        //     g.links(n).into_iter().sorted()
        // };
        // but it's uglier in that I need to explicitly pass in the SquareGrid ref all the time
        macro_rules! links_sorted {
            ($x:expr) => (g.links($x).into_iter().sorted())
        }
        // Testing that the order of the arguments to `is_linked` does not matter
        macro_rules! bi_check_linked {
            ($x:expr, $y:expr) => (g.is_linked($x, $y) && g.is_linked($y, $x))
        }

        // a, b and c start with no links
        assert!(!bi_check_linked!(a, b));
        assert!(!bi_check_linked!(a, c));
        assert!(!bi_check_linked!(b, c));
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![]);
        assert_eq!(links_sorted!(c), vec![]);

        g.link(a, b);
        // a - b linked bi-directionally
        assert!(bi_check_linked!(a, b));
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a]);

        g.link(b, c);
        // a - b still linked bi-directionally after linking b - c
        // b linked to a & c bi-directionally
        // c linked to b bi-directionally
        assert!(bi_check_linked!(a, b));
        assert!(bi_check_linked!(b, c));
        assert!(!bi_check_linked!(a, c));
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a, c]);
        assert_eq!(links_sorted!(c), vec![b]);

        // a - b still linked bi-directionally after updating exist link
        assert!(bi_check_linked!(a, b));
        assert!(bi_check_linked!(b, c));
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a, c]);

        // a - b unlinked
        // b still linked to c bi-directionally
        g.unlink(a, b);
        assert!(!bi_check_linked!(a, b));
        assert!(bi_check_linked!(b, c));
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![c]);
        assert_eq!(links_sorted!(c), vec![b]);

        // a, b and c start all unlinked again
        g.unlink(b, c);
        assert!(!bi_check_linked!(a, b));
        assert!(!bi_check_linked!(a, c));
        assert!(!bi_check_linked!(b, c));
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![]);
        assert_eq!(links_sorted!(c), vec![]);
    }

    #[test]
    fn no_self_linked_cycles() {
        let mut g = SquareGrid::new(4);
        let a = GridCoordinate::new(0, 0);
        g.link(a, a);
        assert_eq!(g.links(a), vec![]);
    }

    #[test]
    fn no_parallel_duplicated_linked_cells() {
        let mut g = SquareGrid::new(4);
        let a = GridCoordinate::new(0, 0);
        let b = GridCoordinate::new(0, 1);
        g.link(a, b);
        g.link(a, b);
        assert_eq!(g.links(a), vec![b]);
        assert_eq!(g.links(b), vec![a]);

        g.unlink(a, b);
        assert_eq!(g.links(a), vec![]);
        assert_eq!(g.links(b), vec![]);
    }
}
<|endoftext|>