<commit_before>use crate::intrinsics;
use crate::masks::*;
use crate::vector::ptr::{SimdConstPtr, SimdMutPtr};
use crate::vector::*;

/// A representation of a vector as an "array" with indices, implementing
/// operations applicable to any vector type based solely on "having lanes",
/// and describing relationships between vector and scalar types.
pub trait SimdArray<const LANES: usize>: crate::LanesAtMost32
where
    SimdUsize<LANES>: crate::LanesAtMost32,
    SimdIsize<LANES>: crate::LanesAtMost32,
    MaskSize<LANES>: crate::Mask,
    Self: Sized,
{
    /// The scalar type in every lane of this vector type.
    type Scalar: Copy + Sized;

    /// Generates a SIMD vector with the same value in every lane.
    #[must_use]
    fn splat(val: Self::Scalar) -> Self;

    /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
    /// If an index is out of bounds, that lane instead selects the value from the "or" vector.
    /// ```
    /// # use core_simd::*;
    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
    /// let alt = SimdI32::from_array([-5, -4, -3, -2]);
    ///
    /// let result = SimdI32::<4>::gather_or(&vec, idxs, alt); // Note the lane that is out-of-bounds.
    /// assert_eq!(result, SimdI32::from_array([-5, 13, 10, 15]));
    /// ```
    #[must_use]
    #[inline]
    fn gather_or(slice: &[Self::Scalar], idxs: SimdUsize<LANES>, or: Self) -> Self {
        Self::gather_select(slice, MaskSize::splat(true), idxs, or)
    }

    /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
    /// Out-of-bounds indices instead use the default value for that lane (0).
    /// ```
    /// # use core_simd::*;
    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
    ///
    /// let result = SimdI32::<4>::gather_or_default(&vec, idxs); // Note the lane that is out-of-bounds.
    /// assert_eq!(result, SimdI32::from_array([0, 13, 10, 15]));
    /// ```
    #[must_use]
    #[inline]
    fn gather_or_default(slice: &[Self::Scalar], idxs: SimdUsize<LANES>) -> Self
    where
        Self::Scalar: Default,
    {
        Self::gather_or(slice, idxs, Self::splat(Self::Scalar::default()))
    }

    /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
    /// Out-of-bounds or masked indices instead select the value from the "or" vector.
    /// ```
    /// # use core_simd::*;
    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
    /// let alt = SimdI32::from_array([-5, -4, -3, -2]);
    /// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane.
    ///
    /// let result = SimdI32::<4>::gather_select(&vec, mask, idxs, alt); // Note the lane that is out-of-bounds.
    /// assert_eq!(result, SimdI32::from_array([-5, 13, 10, -2]));
    /// ```
    #[must_use]
    #[inline]
    fn gather_select(
        slice: &[Self::Scalar],
        mask: MaskSize<LANES>,
        idxs: SimdUsize<LANES>,
        or: Self,
    ) -> Self {
        let mask = (mask & idxs.lanes_lt(SimdUsize::splat(slice.len()))).to_int();
        let base_ptr = SimdConstPtr::splat(slice.as_ptr());
        // Ferris forgive me, I have done pointer arithmetic here.
        let ptrs = base_ptr.wrapping_add(idxs);
        // SAFETY: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah
        unsafe { intrinsics::simd_gather(or, ptrs, mask) }
    }

    /// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
    /// Out-of-bounds indices are not written.
    /// ```
    /// # use core_simd::*;
    /// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
    /// let vals = SimdI32::from_array([-5, -4, -3, -2]);
    ///
    /// vals.scatter(&mut vec, idxs);
    /// assert_eq!(vec, vec![-3, 11, 12, -4, 14, -2, 16, 17, 18]);
    /// ```
    #[inline]
    fn scatter(self, slice: &mut [Self::Scalar], idxs: SimdUsize<LANES>) {
        self.scatter_select(slice, MaskSize::splat(true), idxs)
    }

    /// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
    /// Out-of-bounds or masked indices are not written.
    /// ```
    /// # use core_simd::*;
    /// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
    /// let vals = SimdI32::from_array([-5, -4, -3, -2]);
    /// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane.
    ///
    /// vals.scatter_select(&mut vec, mask, idxs);
    /// assert_eq!(vec, vec![-3, 11, 12, -4, 14, 15, 16, 17, 18]);
    /// ```
    #[inline]
    fn scatter_select(
        self,
        slice: &mut [Self::Scalar],
        mask: MaskSize<LANES>,
        idxs: SimdUsize<LANES>,
    ) {
        // We must construct our scatter mask before we derive a pointer!
        let mask = (mask & idxs.lanes_lt(SimdUsize::splat(slice.len()))).to_int();
        // SAFETY: This block works with *mut T derived from &mut 'a [T],
        // which means it is delicate in Rust's borrowing model, circa 2021:
        // &mut 'a [T] asserts uniqueness, so deriving &'a [T] invalidates live *mut Ts!
        // Even though this block is largely safe methods, it must be almost exactly this way
        // to prevent invalidating the raw ptrs while they're live.
        // Thus, entering this block requires all values to use being already ready:
        // 0. idxs we want to write to, which are used to construct the mask.
        // 1. mask, which depends on an initial &'a [T] and the idxs.
        // 2. actual values to scatter (self).
        // 3. &mut [T] which will become our base ptr.
        unsafe {
            // Now Entering ☢ *mut T Zone
            let base_ptr = SimdMutPtr::splat(slice.as_mut_ptr());
            // Ferris forgive me, I have done pointer arithmetic here.
            let ptrs = base_ptr.wrapping_add(idxs);
            // The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah
            intrinsics::simd_scatter(self, ptrs, mask)
            // Cleared ☢ *mut T Zone
        }
    }
}

macro_rules! impl_simdarray_for {
    ($simd:ident {type Scalar = $scalar:ident;}) => {
        impl<const LANES: usize> SimdArray<LANES> for $simd<LANES>
            where SimdUsize<LANES>: crate::LanesAtMost32,
            SimdIsize<LANES>: crate::LanesAtMost32,
            MaskSize<LANES>: crate::Mask,
            Self: crate::LanesAtMost32,
        {
            type Scalar = $scalar;

            #[must_use]
            #[inline]
            fn splat(val: Self::Scalar) -> Self {
                [val; LANES].into()
            }
        }
    };

    ($simd:ident $impl:tt) => {
        impl<const LANES: usize> SimdArray<LANES> for $simd<LANES>
            where SimdUsize<LANES>: crate::LanesAtMost32,
            SimdIsize<LANES>: crate::LanesAtMost32,
            MaskSize<LANES>: crate::Mask,
            Self: crate::LanesAtMost32,
        $impl
    }
}

impl_simdarray_for! {
    SimdUsize {
        type Scalar = usize;
    }
}

impl_simdarray_for! {
    SimdIsize {
        type Scalar = isize;
    }
}

impl_simdarray_for! {
    SimdI8 {
        type Scalar = i8;
    }
}

impl_simdarray_for! {
    SimdI16 {
        type Scalar = i16;
    }
}

impl_simdarray_for! {
    SimdI32 {
        type Scalar = i32;
    }
}

impl_simdarray_for! {
    SimdI64 {
        type Scalar = i64;
    }
}

impl_simdarray_for! {
    SimdU8 {
        type Scalar = u8;
    }
}

impl_simdarray_for! {
    SimdU16 {
        type Scalar = u16;
    }
}

impl_simdarray_for! {
    SimdU32 {
        type Scalar = u32;
    }
}

impl_simdarray_for! {
    SimdU64 {
        type Scalar = u64;
    }
}

impl_simdarray_for! {
    SimdF32 {
        type Scalar = f32;
    }
}

impl_simdarray_for! {
    SimdF64 {
        type Scalar = f64;
    }
}
<commit_msg>Add assoc const SimdArray::LANES<commit_after>use crate::intrinsics;
use crate::masks::*;
use crate::vector::ptr::{SimdConstPtr, SimdMutPtr};
use crate::vector::*;

/// A representation of a vector as an "array" with indices, implementing
/// operations applicable to any vector type based solely on "having lanes",
/// and describing relationships between vector and scalar types.
pub trait SimdArray<const LANES: usize>: crate::LanesAtMost32
where
    SimdUsize<LANES>: crate::LanesAtMost32,
    SimdIsize<LANES>: crate::LanesAtMost32,
    MaskSize<LANES>: crate::Mask,
    Self: Sized,
{
    /// The scalar type in every lane of this vector type.
    type Scalar: Copy + Sized;
    /// The number of lanes for this vector.
    const LANES: usize = LANES;

    /// Generates a SIMD vector with the same value in every lane.
    #[must_use]
    fn splat(val: Self::Scalar) -> Self;

    /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
    /// If an index is out of bounds, that lane instead selects the value from the "or" vector.
    /// ```
    /// # use core_simd::*;
    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
    /// let alt = SimdI32::from_array([-5, -4, -3, -2]);
    ///
    /// let result = SimdI32::<4>::gather_or(&vec, idxs, alt); // Note the lane that is out-of-bounds.
    /// assert_eq!(result, SimdI32::from_array([-5, 13, 10, 15]));
    /// ```
    #[must_use]
    #[inline]
    fn gather_or(slice: &[Self::Scalar], idxs: SimdUsize<LANES>, or: Self) -> Self {
        Self::gather_select(slice, MaskSize::splat(true), idxs, or)
    }

    /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
    /// Out-of-bounds indices instead use the default value for that lane (0).
    /// ```
    /// # use core_simd::*;
    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
    ///
    /// let result = SimdI32::<4>::gather_or_default(&vec, idxs); // Note the lane that is out-of-bounds.
    /// assert_eq!(result, SimdI32::from_array([0, 13, 10, 15]));
    /// ```
    #[must_use]
    #[inline]
    fn gather_or_default(slice: &[Self::Scalar], idxs: SimdUsize<LANES>) -> Self
    where
        Self::Scalar: Default,
    {
        Self::gather_or(slice, idxs, Self::splat(Self::Scalar::default()))
    }

    /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
    /// Out-of-bounds or masked indices instead select the value from the "or" vector.
    /// ```
    /// # use core_simd::*;
    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
    /// let alt = SimdI32::from_array([-5, -4, -3, -2]);
    /// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane.
    ///
    /// let result = SimdI32::<4>::gather_select(&vec, mask, idxs, alt); // Note the lane that is out-of-bounds.
    /// assert_eq!(result, SimdI32::from_array([-5, 13, 10, -2]));
    /// ```
    #[must_use]
    #[inline]
    fn gather_select(
        slice: &[Self::Scalar],
        mask: MaskSize<LANES>,
        idxs: SimdUsize<LANES>,
        or: Self,
    ) -> Self {
        let mask = (mask & idxs.lanes_lt(SimdUsize::splat(slice.len()))).to_int();
        let base_ptr = SimdConstPtr::splat(slice.as_ptr());
        // Ferris forgive me, I have done pointer arithmetic here.
        let ptrs = base_ptr.wrapping_add(idxs);
        // SAFETY: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah
        unsafe { intrinsics::simd_gather(or, ptrs, mask) }
    }

    /// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
    /// Out-of-bounds indices are not written.
    /// ```
    /// # use core_simd::*;
    /// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
    /// let vals = SimdI32::from_array([-5, -4, -3, -2]);
    ///
    /// vals.scatter(&mut vec, idxs);
    /// assert_eq!(vec, vec![-3, 11, 12, -4, 14, -2, 16, 17, 18]);
    /// ```
    #[inline]
    fn scatter(self, slice: &mut [Self::Scalar], idxs: SimdUsize<LANES>) {
        self.scatter_select(slice, MaskSize::splat(true), idxs)
    }

    /// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
    /// Out-of-bounds or masked indices are not written.
    /// ```
    /// # use core_simd::*;
    /// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
    /// let vals = SimdI32::from_array([-5, -4, -3, -2]);
    /// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane.
    ///
    /// vals.scatter_select(&mut vec, mask, idxs);
    /// assert_eq!(vec, vec![-3, 11, 12, -4, 14, 15, 16, 17, 18]);
    /// ```
    #[inline]
    fn scatter_select(
        self,
        slice: &mut [Self::Scalar],
        mask: MaskSize<LANES>,
        idxs: SimdUsize<LANES>,
    ) {
        // We must construct our scatter mask before we derive a pointer!
        let mask = (mask & idxs.lanes_lt(SimdUsize::splat(slice.len()))).to_int();
        // SAFETY: This block works with *mut T derived from &mut 'a [T],
        // which means it is delicate in Rust's borrowing model, circa 2021:
        // &mut 'a [T] asserts uniqueness, so deriving &'a [T] invalidates live *mut Ts!
        // Even though this block is largely safe methods, it must be almost exactly this way
        // to prevent invalidating the raw ptrs while they're live.
        // Thus, entering this block requires all values to use being already ready:
        // 0. idxs we want to write to, which are used to construct the mask.
        // 1. mask, which depends on an initial &'a [T] and the idxs.
        // 2. actual values to scatter (self).
        // 3. &mut [T] which will become our base ptr.
        unsafe {
            // Now Entering ☢ *mut T Zone
            let base_ptr = SimdMutPtr::splat(slice.as_mut_ptr());
            // Ferris forgive me, I have done pointer arithmetic here.
            let ptrs = base_ptr.wrapping_add(idxs);
            // The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah
            intrinsics::simd_scatter(self, ptrs, mask)
            // Cleared ☢ *mut T Zone
        }
    }
}

macro_rules! impl_simdarray_for {
    ($simd:ident {type Scalar = $scalar:ident;}) => {
        impl<const LANES: usize> SimdArray<LANES> for $simd<LANES>
            where SimdUsize<LANES>: crate::LanesAtMost32,
            SimdIsize<LANES>: crate::LanesAtMost32,
            MaskSize<LANES>: crate::Mask,
            Self: crate::LanesAtMost32,
        {
            type Scalar = $scalar;

            #[must_use]
            #[inline]
            fn splat(val: Self::Scalar) -> Self {
                [val; LANES].into()
            }
        }
    };

    ($simd:ident $impl:tt) => {
        impl<const LANES: usize> SimdArray<LANES> for $simd<LANES>
            where SimdUsize<LANES>: crate::LanesAtMost32,
            SimdIsize<LANES>: crate::LanesAtMost32,
            MaskSize<LANES>: crate::Mask,
            Self: crate::LanesAtMost32,
        $impl
    }
}

impl_simdarray_for! {
    SimdUsize {
        type Scalar = usize;
    }
}

impl_simdarray_for! {
    SimdIsize {
        type Scalar = isize;
    }
}

impl_simdarray_for! {
    SimdI8 {
        type Scalar = i8;
    }
}

impl_simdarray_for! {
    SimdI16 {
        type Scalar = i16;
    }
}

impl_simdarray_for! {
    SimdI32 {
        type Scalar = i32;
    }
}

impl_simdarray_for! {
    SimdI64 {
        type Scalar = i64;
    }
}

impl_simdarray_for! {
    SimdU8 {
        type Scalar = u8;
    }
}

impl_simdarray_for! {
    SimdU16 {
        type Scalar = u16;
    }
}

impl_simdarray_for! {
    SimdU32 {
        type Scalar = u32;
    }
}

impl_simdarray_for! {
    SimdU64 {
        type Scalar = u64;
    }
}

impl_simdarray_for! {
    SimdF32 {
        type Scalar = f32;
    }
}

impl_simdarray_for! {
    SimdF64 {
        type Scalar = f64;
    }
}
<|endoftext|>


<commit_before>use crate::{SourceFileNode, SyntaxKind, SyntaxNodeRef, WalkEvent};
use std::fmt::Write;
use std::str;

/// Parse a file and create a string representation of the resulting parse tree.
pub fn dump_tree(syntax: SyntaxNodeRef) -> String {
    let mut errors: Vec<_> = syntax.root_data().to_vec();
    errors.sort_by_key(|e| e.offset());
    let mut err_pos = 0;
    let mut level = 0;
    let mut buf = String::new();
    macro_rules! indent {
        () => {
            for _ in 0..level {
                buf.push_str("  ");
            }
        };
    }

    for event in syntax.preorder() {
        match event {
            WalkEvent::Enter(node) => {
                indent!();
                writeln!(buf, "{:?}", node).unwrap();
                if node.first_child().is_none() {
                    let off = node.range().end();
                    while err_pos < errors.len() && errors[err_pos].offset() <= off {
                        indent!();
                        writeln!(buf, "err: `{}`", errors[err_pos]).unwrap();
                        err_pos += 1;
                    }
                }
                level += 1;
            }
            WalkEvent::Leave(_) => level -= 1,
        }
    }

    assert_eq!(level, 0);
    for err in errors[err_pos..].iter() {
        writeln!(buf, "err: `{}`", err).unwrap();
    }

    buf
}

pub fn check_fuzz_invariants(text: &str) {
    let file = SourceFileNode::parse(text);
    let root = file.syntax();
    validate_block_structure(root);
    let _ = file.ast();
    let _ = file.errors();
}

pub(crate) fn validate_block_structure(root: SyntaxNodeRef) {
    let mut stack = Vec::new();
    for node in root.descendants() {
        match node.kind() {
            SyntaxKind::L_CURLY => stack.push(node),
            SyntaxKind::R_CURLY => {
                if let Some(pair) = stack.pop() {
                    assert_eq!(
                        node.parent(),
                        pair.parent(),
                        "\nunpaired curleys:\n{}\n{}\n",
                        root.text(),
                        dump_tree(root),
                    );
                    assert!(
                        node.next_sibling().is_none() && pair.prev_sibling().is_none(),
                        "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
                        node,
                        root.text(),
                        node.text(),
                    );
                }
            }
            _ => (),
        }
    }
}
<commit_msg>dump validation errors<commit_after>use crate::{SourceFileNode, SyntaxKind, SyntaxNodeRef, WalkEvent, AstNode};
use std::fmt::Write;
use std::str;

/// Parse a file and create a string representation of the resulting parse tree.
pub fn dump_tree(syntax: SyntaxNodeRef) -> String {
    let mut errors: Vec<_> = match syntax.ancestors().find_map(SourceFileNode::cast) {
        Some(file) => file.owned().errors(),
        None => syntax.root_data().to_vec(),
    };
    errors.sort_by_key(|e| e.offset());
    let mut err_pos = 0;
    let mut level = 0;
    let mut buf = String::new();
    macro_rules! indent {
        () => {
            for _ in 0..level {
                buf.push_str("  ");
            }
        };
    }

    for event in syntax.preorder() {
        match event {
            WalkEvent::Enter(node) => {
                indent!();
                writeln!(buf, "{:?}", node).unwrap();
                if node.first_child().is_none() {
                    let off = node.range().end();
                    while err_pos < errors.len() && errors[err_pos].offset() <= off {
                        indent!();
                        writeln!(buf, "err: `{}`", errors[err_pos]).unwrap();
                        err_pos += 1;
                    }
                }
                level += 1;
            }
            WalkEvent::Leave(_) => level -= 1,
        }
    }

    assert_eq!(level, 0);
    for err in errors[err_pos..].iter() {
        writeln!(buf, "err: `{}`", err).unwrap();
    }

    buf
}

pub fn check_fuzz_invariants(text: &str) {
    let file = SourceFileNode::parse(text);
    let root = file.syntax();
    validate_block_structure(root);
    let _ = file.ast();
    let _ = file.errors();
}

pub(crate) fn validate_block_structure(root: SyntaxNodeRef) {
    let mut stack = Vec::new();
    for node in root.descendants() {
        match node.kind() {
            SyntaxKind::L_CURLY => stack.push(node),
            SyntaxKind::R_CURLY => {
                if let Some(pair) = stack.pop() {
                    assert_eq!(
                        node.parent(),
                        pair.parent(),
                        "\nunpaired curleys:\n{}\n{}\n",
                        root.text(),
                        dump_tree(root),
                    );
                    assert!(
                        node.next_sibling().is_none() && pair.prev_sibling().is_none(),
                        "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
                        node,
                        root.text(),
                        node.text(),
                    );
                }
            }
            _ => (),
        }
    }
}
<|endoftext|>


<commit_before>use expression::{AsExpression, Expression};
use super::operators::*;
use types::{Array, Text};

pub trait PgExpressionMethods: Expression + Sized {
    /// Creates a PostgreSQL `IS NOT DISTINCT FROM` expression. This behaves
    /// identically to the `=` operator, except that `NULL` is treated as a
    /// normal value.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let connection = establish_connection();
    /// let distinct = users.select(id).filter(name.is_distinct_from("Sean"));
    /// let not_distinct = users.select(id).filter(name.is_not_distinct_from("Sean"));
    /// assert_eq!(Ok(2), distinct.first(&connection));
    /// assert_eq!(Ok(1), not_distinct.first(&connection));
    /// # }
    /// ```
    fn is_not_distinct_from<T>(self, other: T) -> IsNotDistinctFrom<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        IsNotDistinctFrom::new(self, other.as_expression())
    }

    /// Creates a PostgreSQL `IS DISTINCT FROM` expression. This behaves
    /// identically to the `!=` operator, except that `NULL` is treated as a
    /// normal value.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let connection = establish_connection();
    /// let distinct = users.select(id).filter(name.is_distinct_from("Sean"));
    /// let not_distinct = users.select(id).filter(name.is_not_distinct_from("Sean"));
    /// assert_eq!(Ok(2), distinct.first(&connection));
    /// assert_eq!(Ok(1), not_distinct.first(&connection));
    /// # }
    /// ```
    fn is_distinct_from<T>(self, other: T) -> IsDistinctFrom<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        IsDistinctFrom::new(self, other.as_expression())
    }
}

impl<T: Expression> PgExpressionMethods for T {}

use super::date_and_time::{AtTimeZone, DateTimeLike};
use types::VarChar;

#[doc(hidden)]
pub trait PgTimestampExpressionMethods: Expression + Sized {
    /// Returns a PostgreSQL "AT TIME ZONE" expression
    fn at_time_zone<T>(self, timezone: T) -> AtTimeZone<Self, T::Expression>
    where
        T: AsExpression<VarChar>,
    {
        AtTimeZone::new(self, timezone.as_expression())
    }
}

impl<T: Expression> PgTimestampExpressionMethods for T
where
    T::SqlType: DateTimeLike,
{
}

pub trait ArrayExpressionMethods<ST>: Expression<SqlType = Array<ST>> + Sized {
    /// Compares two arrays for common elements, using the `&&` operator in
    /// the final SQL
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     posts {
    /// #         id -> Integer,
    /// #         tags -> Array<VarChar>,
    /// #     }
    /// # }
    /// #
    /// # // FIXME: We shouldn't need to define a users table here
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # #[derive(Insertable)]
    /// # #[table_name="posts"]
    /// # struct NewPost<'a> { tags: Vec<&'a str> }
    /// #
    /// # fn main() {
    /// #     use self::posts::dsl::*;
    /// #     let conn = establish_connection();
    /// #     conn.execute("DROP TABLE IF EXISTS posts").unwrap();
    /// #     conn.execute("CREATE TABLE posts (id SERIAL PRIMARY KEY, tags TEXT[] NOT NULL)").unwrap();
    /// #
    /// diesel::insert_into(posts)
    ///     .values(&vec![
    ///         NewPost { tags: vec!["cool", "awesome"] },
    ///         NewPost { tags: vec!["awesome", "great"] },
    ///         NewPost { tags: vec!["cool", "great"] },
    ///     ])
    ///     .execute(&conn)
    ///     .unwrap();
    ///
    /// let query = posts.select(id).filter(tags.overlaps_with(vec!["horrid", "cool"]));
    /// assert_eq!(Ok(vec![1, 3]), query.load(&conn));
    ///
    /// let query = posts.select(id).filter(tags.overlaps_with(vec!["cool", "great"]));
    /// assert_eq!(Ok(vec![1, 2, 3]), query.load(&conn));
    ///
    /// let query = posts.select(id).filter(tags.overlaps_with(vec!["horrid"]));
    /// assert_eq!(Ok(Vec::new()), query.load::<i32>(&conn));
    /// # }
    /// ```
    fn overlaps_with<T>(self, other: T) -> OverlapsWith<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        OverlapsWith::new(self, other.as_expression())
    }

    /// Compares whether an array contains another array, using the `@>` operator.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     posts {
    /// #         id -> Integer,
    /// #         tags -> Array<VarChar>,
    /// #     }
    /// # }
    /// #
    /// # // FIXME: We shouldn't need to define a users table here
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # #[derive(Insertable)]
    /// # #[table_name="posts"]
    /// # struct NewPost<'a> { tags: Vec<&'a str> }
    /// #
    /// # fn main() {
    /// #     use self::posts::dsl::*;
    /// #     let conn = establish_connection();
    /// #     conn.execute("DROP TABLE IF EXISTS posts").unwrap();
    /// #     conn.execute("CREATE TABLE posts (id SERIAL PRIMARY KEY, tags TEXT[] NOT NULL)").unwrap();
    /// #
    /// diesel::insert_into(posts)
    ///     .values(&vec![
    ///         NewPost { tags: vec!["cool", "awesome"] },
    ///     ])
    ///     .execute(&conn)
    ///     .unwrap();
    ///
    /// let query = posts.select(id).filter(tags.contains(vec!["cool"]));
    /// assert_eq!(Ok(vec![1]), query.load(&conn));
    ///
    /// let query = posts.select(id).filter(tags.contains(vec!["cool", "amazing"]));
    /// assert_eq!(Ok(Vec::new()), query.load::<i32>(&conn));
    /// # }
    /// ```
    fn contains<T>(self, other: T) -> Contains<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        Contains::new(self, other.as_expression())
    }

    /// Compares whether an array is contained by another array, using the `<@` operator.
    /// This is the opposite of `contains`
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     posts {
    /// #         id -> Integer,
    /// #         tags -> Array<VarChar>,
    /// #     }
    /// # }
    /// #
    /// # // FIXME: We shouldn't need to define a users table here
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # #[derive(Insertable)]
    /// # #[table_name="posts"]
    /// # struct NewPost<'a> { tags: Vec<&'a str> }
    /// #
    /// # fn main() {
    /// #     use self::posts::dsl::*;
    /// #     let conn = establish_connection();
    /// #     conn.execute("DROP TABLE IF EXISTS posts").unwrap();
    /// #     conn.execute("CREATE TABLE posts (id SERIAL PRIMARY KEY, tags TEXT[] NOT NULL)").unwrap();
    /// #
    /// diesel::insert_into(posts)
    ///     .values(&vec![
    ///         NewPost { tags: vec!["cool", "awesome"] },
    ///     ])
    ///     .execute(&conn)
    ///     .unwrap();
    ///
    /// let query = posts.select(id).filter(tags.is_contained_by(vec!["cool", "awesome", "amazing"]));
    /// assert_eq!(Ok(vec![1]), query.load(&conn));
    ///
    /// let query = posts.select(id).filter(tags.is_contained_by(vec!["cool"]));
    /// assert_eq!(Ok(Vec::new()), query.load::<i32>(&conn));
    /// # }
    /// ```
    fn is_contained_by<T>(self, other: T) -> IsContainedBy<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        IsContainedBy::new(self, other.as_expression())
    }
}

impl<T, ST> ArrayExpressionMethods<ST> for T
where
    T: Expression<SqlType = Array<ST>>,
{
}

use expression::operators::{Asc, Desc};

pub trait SortExpressionMethods: Sized {
    /// Specify that nulls should come before other values in this ordering.
    /// Normally, nulls come last when sorting in ascending order and first
    /// when sorting in descending order.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # table! {
    /// #     foos {
    /// #         id -> Integer,
    /// #         foo -> Nullable<Integer>,
    /// #     }
    /// # }
    /// #
    /// # fn main() {
    /// #     let connection = connection_no_data();
    /// #     connection.execute("DROP TABLE IF EXISTS foos").unwrap();
    /// connection.execute("CREATE TABLE foos (id SERIAL PRIMARY KEY, foo INTEGER)").unwrap();
    /// connection.execute("INSERT INTO foos (foo) VALUES (NULL), (1), (2)").unwrap();
    ///
    /// #     use self::foos::dsl::*;
    /// assert_eq!(Ok(vec![Some(1), Some(2), None]),
    ///            foos.select(foo).order(foo.asc()).load(&connection));
    /// assert_eq!(Ok(vec![None, Some(1), Some(2)]),
    ///            foos.select(foo).order(foo.asc().nulls_first()).load(&connection));
    /// #     connection.execute("DROP TABLE foos").unwrap();
    /// # }
    /// ```
    fn nulls_first(self) -> NullsFirst<Self> {
        NullsFirst::new(self)
    }

    /// Specify that nulls should come after other values in this ordering.
    /// Normally, nulls come last when sorting in ascending order and first
    /// when sorting in descending order.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # table! {
    /// #     foos {
    /// #         id -> Integer,
    /// #         foo -> Nullable<Integer>,
    /// #     }
    /// # }
    /// #
    /// # fn main() {
    /// #     let connection = connection_no_data();
    /// #     connection.execute("DROP TABLE IF EXISTS foos").unwrap();
    /// connection.execute("CREATE TABLE foos (id SERIAL PRIMARY KEY, foo INTEGER)").unwrap();
    /// connection.execute("INSERT INTO foos (foo) VALUES (NULL), (1), (2)").unwrap();
    ///
    /// #     use self::foos::dsl::*;
    /// assert_eq!(Ok(vec![None, Some(2), Some(1)]),
    ///            foos.select(foo).order(foo.desc()).load(&connection));
    /// assert_eq!(Ok(vec![Some(2), Some(1), None]),
    ///            foos.select(foo).order(foo.desc().nulls_last()).load(&connection));
    /// #     connection.execute("DROP TABLE foos").unwrap();
    /// # }
    /// ```
    fn nulls_last(self) -> NullsLast<Self> {
        NullsLast::new(self)
    }
}

impl<T> SortExpressionMethods for Asc<T> {}

impl<T> SortExpressionMethods for Desc<T> {}

pub trait PgTextExpressionMethods: Expression<SqlType = Text> + Sized {
    /// Returns a SQL `ILIKE` expression
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// # use schema::users;
    /// #
    /// # fn main() {
    /// #     use schema::users::dsl::*;
    /// #     let connection = establish_connection();
    /// #
    /// let like_sean = users
    ///     .select(name)
    ///     .filter(name.ilike("sean"))
    ///     .get_results::<String>(&connection)
    ///     .expect("Failed");
    ///
    /// let expected = vec!["Sean".to_string()];
    ///
    /// assert_eq!(expected, like_sean);
    /// # }
    /// ```
    fn ilike<T: AsExpression<Text>>(self, other: T) -> ILike<Self, T::Expression> {
        ILike::new(self.as_expression(), other.as_expression())
    }

    /// Returns a SQL `NOT ILIKE` expression
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// # use schema::users;
    /// #
    /// # fn main() {
    /// #     use schema::users::dsl::*;
    /// #     let connection = establish_connection();
    /// #
    /// let not_like_sean = users
    ///     .select(name)
    ///     .filter(name.not_ilike("sean"))
    ///     .get_results::<String>(&connection)
    ///     .expect("Failed");
    ///
    /// let expected = vec!["Tess".to_string()];
    ///
    /// assert_eq!(expected, not_like_sean);
    /// # }
    /// ```
    fn not_ilike<T: AsExpression<Text>>(self, other: T) -> NotILike<Self, T::Expression> {
        NotILike::new(self.as_expression(), other.as_expression())
    }
}

impl<T: Expression<SqlType = Text>> PgTextExpressionMethods for T {}
<commit_msg>address FIXME by bringing users schema into scope<commit_after>use expression::{AsExpression, Expression};
use super::operators::*;
use types::{Array, Text};

pub trait PgExpressionMethods: Expression + Sized {
    /// Creates a PostgreSQL `IS NOT DISTINCT FROM` expression. This behaves
    /// identically to the `=` operator, except that `NULL` is treated as a
    /// normal value.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let connection = establish_connection();
    /// let distinct = users.select(id).filter(name.is_distinct_from("Sean"));
    /// let not_distinct = users.select(id).filter(name.is_not_distinct_from("Sean"));
    /// assert_eq!(Ok(2), distinct.first(&connection));
    /// assert_eq!(Ok(1), not_distinct.first(&connection));
    /// # }
    /// ```
    fn is_not_distinct_from<T>(self, other: T) -> IsNotDistinctFrom<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        IsNotDistinctFrom::new(self, other.as_expression())
    }

    /// Creates a PostgreSQL `IS DISTINCT FROM` expression. This behaves
    /// identically to the `!=` operator, except that `NULL` is treated as a
    /// normal value.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let connection = establish_connection();
    /// let distinct = users.select(id).filter(name.is_distinct_from("Sean"));
    /// let not_distinct = users.select(id).filter(name.is_not_distinct_from("Sean"));
    /// assert_eq!(Ok(2), distinct.first(&connection));
    /// assert_eq!(Ok(1), not_distinct.first(&connection));
    /// # }
    /// ```
    fn is_distinct_from<T>(self, other: T) -> IsDistinctFrom<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        IsDistinctFrom::new(self, other.as_expression())
    }
}

impl<T: Expression> PgExpressionMethods for T {}

use super::date_and_time::{AtTimeZone, DateTimeLike};
use types::VarChar;

#[doc(hidden)]
pub trait PgTimestampExpressionMethods: Expression + Sized {
    /// Returns a PostgreSQL "AT TIME ZONE" expression
    fn at_time_zone<T>(self, timezone: T) -> AtTimeZone<Self, T::Expression>
    where
        T: AsExpression<VarChar>,
    {
        AtTimeZone::new(self, timezone.as_expression())
    }
}

impl<T: Expression> PgTimestampExpressionMethods for T
where
    T::SqlType: DateTimeLike,
{
}

pub trait ArrayExpressionMethods<ST>: Expression<SqlType = Array<ST>> + Sized {
    /// Compares two arrays for common elements, using the `&&` operator in
    /// the final SQL
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// # use schema::users;
    /// #
    /// # table! {
    /// #     posts {
    /// #         id -> Integer,
    /// #         tags -> Array<VarChar>,
    /// #     }
    /// # }
    /// #
    /// # #[derive(Insertable)]
    /// # #[table_name="posts"]
    /// # struct NewPost<'a> { tags: Vec<&'a str> }
    /// #
    /// # fn main() {
    /// #     use self::posts::dsl::*;
    /// #     let conn = establish_connection();
    /// #     conn.execute("DROP TABLE IF EXISTS posts").unwrap();
    /// #     conn.execute("CREATE TABLE posts (id SERIAL PRIMARY KEY, tags TEXT[] NOT NULL)").unwrap();
    /// #
    /// diesel::insert_into(posts)
    ///     .values(&vec![
    ///         NewPost { tags: vec!["cool", "awesome"] },
    ///         NewPost { tags: vec!["awesome", "great"] },
    ///         NewPost { tags: vec!["cool", "great"] },
    ///     ])
    ///     .execute(&conn)
    ///     .unwrap();
    ///
    /// let query = posts.select(id).filter(tags.overlaps_with(vec!["horrid", "cool"]));
    /// assert_eq!(Ok(vec![1, 3]), query.load(&conn));
    ///
    /// let query = posts.select(id).filter(tags.overlaps_with(vec!["cool", "great"]));
    /// assert_eq!(Ok(vec![1, 2, 3]), query.load(&conn));
    ///
    /// let query = posts.select(id).filter(tags.overlaps_with(vec!["horrid"]));
    /// assert_eq!(Ok(Vec::new()), query.load::<i32>(&conn));
    /// # }
    /// ```
    fn overlaps_with<T>(self, other: T) -> OverlapsWith<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        OverlapsWith::new(self, other.as_expression())
    }

    /// Compares whether an array contains another array, using the `@>` operator.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// # use schema::users;
    /// #
    /// # table! {
    /// #     posts {
    /// #         id -> Integer,
    /// #         tags -> Array<VarChar>,
    /// #     }
    /// # }
    /// #
    /// # #[derive(Insertable)]
    /// # #[table_name="posts"]
    /// # struct NewPost<'a> { tags: Vec<&'a str> }
    /// #
    /// # fn main() {
    /// #     use self::posts::dsl::*;
    /// #     let conn = establish_connection();
    /// #     conn.execute("DROP TABLE IF EXISTS posts").unwrap();
    /// #     conn.execute("CREATE TABLE posts (id SERIAL PRIMARY KEY, tags TEXT[] NOT NULL)").unwrap();
    /// #
    /// diesel::insert_into(posts)
    ///     .values(&vec![
    ///         NewPost { tags: vec!["cool", "awesome"] },
    ///     ])
    ///     .execute(&conn)
    ///     .unwrap();
    ///
    /// let query = posts.select(id).filter(tags.contains(vec!["cool"]));
    /// assert_eq!(Ok(vec![1]), query.load(&conn));
    ///
    /// let query = posts.select(id).filter(tags.contains(vec!["cool", "amazing"]));
    /// assert_eq!(Ok(Vec::new()), query.load::<i32>(&conn));
    /// # }
    /// ```
    fn contains<T>(self, other: T) -> Contains<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        Contains::new(self, other.as_expression())
    }

    /// Compares whether an array is contained by another array, using the `<@` operator.
    /// This is the opposite of `contains`
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// # use schema::users;
    /// #
    /// # table! {
    /// #     posts {
    /// #         id -> Integer,
    /// #         tags -> Array<VarChar>,
    /// #     }
    /// # }
    /// #
    /// # #[derive(Insertable)]
    /// # #[table_name="posts"]
    /// # struct NewPost<'a> { tags: Vec<&'a str> }
    /// #
    /// # fn main() {
    /// #     use self::posts::dsl::*;
    /// #     let conn = establish_connection();
    /// #     conn.execute("DROP TABLE IF EXISTS posts").unwrap();
    /// #     conn.execute("CREATE TABLE posts (id SERIAL PRIMARY KEY, tags TEXT[] NOT NULL)").unwrap();
    /// #
    /// diesel::insert_into(posts)
    ///     .values(&vec![
    ///         NewPost { tags: vec!["cool", "awesome"] },
    ///     ])
    ///     .execute(&conn)
    ///     .unwrap();
    ///
    /// let query = posts.select(id).filter(tags.is_contained_by(vec!["cool", "awesome", "amazing"]));
    /// assert_eq!(Ok(vec![1]), query.load(&conn));
    ///
    /// let query = posts.select(id).filter(tags.is_contained_by(vec!["cool"]));
    /// assert_eq!(Ok(Vec::new()), query.load::<i32>(&conn));
    /// # }
    /// ```
    fn is_contained_by<T>(self, other: T) -> IsContainedBy<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        IsContainedBy::new(self, other.as_expression())
    }
}

impl<T, ST> ArrayExpressionMethods<ST> for T
where
    T: Expression<SqlType = Array<ST>>,
{
}

use expression::operators::{Asc, Desc};

pub trait SortExpressionMethods: Sized {
    /// Specify that nulls should come before other values in this ordering.
    /// Normally, nulls come last when sorting in ascending order and first
    /// when sorting in descending order.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # table! {
    /// #     foos {
    /// #         id -> Integer,
    /// #         foo -> Nullable<Integer>,
    /// #     }
    /// # }
    /// #
    /// # fn main() {
    /// #     let connection = connection_no_data();
    /// #     connection.execute("DROP TABLE IF EXISTS foos").unwrap();
    /// connection.execute("CREATE TABLE foos (id SERIAL PRIMARY KEY, foo INTEGER)").unwrap();
    /// connection.execute("INSERT INTO foos (foo) VALUES (NULL), (1), (2)").unwrap();
    ///
    /// #     use self::foos::dsl::*;
    /// assert_eq!(Ok(vec![Some(1), Some(2), None]),
    ///            foos.select(foo).order(foo.asc()).load(&connection));
    /// assert_eq!(Ok(vec![None, Some(1), Some(2)]),
    ///            foos.select(foo).order(foo.asc().nulls_first()).load(&connection));
    /// #     connection.execute("DROP TABLE foos").unwrap();
    /// # }
    /// ```
    fn nulls_first(self) -> NullsFirst<Self> {
        NullsFirst::new(self)
    }

    /// Specify that nulls should come after other values in this ordering.
    /// Normally, nulls come last when sorting in ascending order and first
    /// when sorting in descending order.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # table! {
    /// #     foos {
    /// #         id -> Integer,
    /// #         foo -> Nullable<Integer>,
    /// #     }
    /// # }
    /// #
    /// # fn main() {
    /// #     let connection = connection_no_data();
    /// #     connection.execute("DROP TABLE IF EXISTS foos").unwrap();
    /// connection.execute("CREATE TABLE foos (id SERIAL PRIMARY KEY, foo INTEGER)").unwrap();
    /// connection.execute("INSERT INTO foos (foo) VALUES (NULL), (1), (2)").unwrap();
    ///
    /// #     use self::foos::dsl::*;
    /// assert_eq!(Ok(vec![None, Some(2), Some(1)]),
    ///            foos.select(foo).order(foo.desc()).load(&connection));
    /// assert_eq!(Ok(vec![Some(2), Some(1), None]),
    ///            foos.select(foo).order(foo.desc().nulls_last()).load(&connection));
    /// #     connection.execute("DROP TABLE foos").unwrap();
    /// # }
    /// ```
    fn nulls_last(self) -> NullsLast<Self> {
        NullsLast::new(self)
    }
}

impl<T> SortExpressionMethods for Asc<T> {}

impl<T> SortExpressionMethods for Desc<T> {}

pub trait PgTextExpressionMethods: Expression<SqlType = Text> + Sized {
    /// Returns a SQL `ILIKE` expression
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// # use schema::users;
    /// #
    /// # fn main() {
    /// #     use schema::users::dsl::*;
    /// #     let connection = establish_connection();
    /// #
    /// let like_sean = users
    ///     .select(name)
    ///     .filter(name.ilike("sean"))
    ///     .get_results::<String>(&connection)
    ///     .expect("Failed");
    ///
    /// let expected = vec!["Sean".to_string()];
    ///
    /// assert_eq!(expected, like_sean);
    /// # }
    /// ```
    fn ilike<T: AsExpression<Text>>(self, other: T) -> ILike<Self, T::Expression> {
        ILike::new(self.as_expression(), other.as_expression())
    }

    /// Returns a SQL `NOT ILIKE` expression
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # #[macro_use] extern crate diesel_codegen;
    /// # include!("../../doctest_setup.rs");
    /// # use schema::users;
    /// #
    /// # fn main() {
    /// #     use schema::users::dsl::*;
    /// #     let connection = establish_connection();
    /// #
    /// let not_like_sean = users
    ///     .select(name)
    ///     .filter(name.not_ilike("sean"))
    ///     .get_results::<String>(&connection)
    ///     .expect("Failed");
    ///
    /// let expected = vec!["Tess".to_string()];
    ///
    /// assert_eq!(expected, not_like_sean);
    /// # }
    /// ```
    fn not_ilike<T: AsExpression<Text>>(self, other: T) -> NotILike<Self, T::Expression> {
        NotILike::new(self.as_expression(), other.as_expression())
    }
}

impl<T: Expression<SqlType = Text>> PgTextExpressionMethods for T {}
<|endoftext|>


<commit_before>use super::vm::StackEntry;
use crate::compiler::lir::ChunkIndex;
use im::HashMap;

/// A self-contained value. Unlike objects, these are not tied to a running VM,
/// which makes them useful for being sent through channels between multiple
/// reference-counted heaps, for example ones running concurrently logically, on
/// other cores, or on different computers.
///
/// VMs can import these values to turn them into heap-contained,
/// reference-counted objects. They can export objects from the heap into
/// self-contained values.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum Value {
    Int(u64),
    Text(String),
    Symbol(String),
    Struct(HashMap<Value, Value>),
    Closure {
        captured: Vec<StackEntry>,
        body: ChunkIndex,
    },
}

impl Value {
    pub fn nothing() -> Value {
        Value::Symbol("Nothing".to_owned())
    }
    pub fn list(items: Vec<Value>) -> Self {
        let items = items
            .into_iter()
            .enumerate()
            .map(|(index, it)| (Value::Int(index as u64), it))
            .collect();
        Value::Struct(items)
    }

    pub fn into_int(self) -> Option<u64> {
        match self {
            Value::Int(int) => Some(int),
            _ => None,
        }
    }
    pub fn into_text(self) -> Option<String> {
        match self {
            Value::Text(text) => Some(text),
            _ => None,
        }
    }
    pub fn into_symbol(self) -> Option<String> {
        match self {
            Value::Symbol(symbol) => Some(symbol),
            _ => None,
        }
    }
    pub fn into_struct(self) -> Option<HashMap<Value, Value>> {
        match self {
            Value::Struct(entries) => Some(entries),
            _ => None,
        }
    }
    pub fn into_closure(self) -> Option<(Vec<StackEntry>, ChunkIndex)> {
        match self {
            Value::Closure { captured, body } => Some((captured, body)),
            _ => None,
        }
    }
}

impl From<u64> for Value {
    fn from(value: u64) -> Self {
        Value::Int(value)
    }
}
impl From<String> for Value {
    fn from(value: String) -> Self {
        Value::Text(value)
    }
}
impl From<bool> for Value {
    fn from(it: bool) -> Self {
        Value::Symbol(if it { "True" } else { "False" }.to_string())
    }
}
<commit_msg>Implement Display for Value<commit_after>use std::fmt::{self, Display, Formatter};

use super::vm::StackEntry;
use crate::compiler::lir::ChunkIndex;
use im::HashMap;
use itertools::Itertools;

/// A self-contained value. Unlike objects, these are not tied to a running VM,
/// which makes them useful for being sent through channels between multiple
/// reference-counted heaps, for example ones running concurrently logically, on
/// other cores, or on different computers.
///
/// VMs can import these values to turn them into heap-contained,
/// reference-counted objects. They can export objects from the heap into
/// self-contained values.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum Value {
    Int(u64),
    Text(String),
    Symbol(String),
    Struct(HashMap<Value, Value>),
    Closure {
        captured: Vec<StackEntry>,
        body: ChunkIndex,
    },
}

impl Value {
    pub fn nothing() -> Value {
        Value::Symbol("Nothing".to_owned())
    }
    pub fn list(items: Vec<Value>) -> Self {
        let items = items
            .into_iter()
            .enumerate()
            .map(|(index, it)| (Value::Int(index as u64), it))
            .collect();
        Value::Struct(items)
    }

    pub fn into_int(self) -> Option<u64> {
        match self {
            Value::Int(int) => Some(int),
            _ => None,
        }
    }
    pub fn into_text(self) -> Option<String> {
        match self {
            Value::Text(text) => Some(text),
            _ => None,
        }
    }
    pub fn into_symbol(self) -> Option<String> {
        match self {
            Value::Symbol(symbol) => Some(symbol),
            _ => None,
        }
    }
    pub fn into_struct(self) -> Option<HashMap<Value, Value>> {
        match self {
            Value::Struct(entries) => Some(entries),
            _ => None,
        }
    }
    pub fn into_closure(self) -> Option<(Vec<StackEntry>, ChunkIndex)> {
        match self {
            Value::Closure { captured, body } => Some((captured, body)),
            _ => None,
        }
    }
}

impl Display for Value {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Value::Int(int) => write!(f, "{}", int),
            Value::Text(text) => write!(f, "{:?}", text),
            Value::Symbol(symbol) => write!(f, "{}", symbol),
            Value::Struct(entries) => write!(
                f,
                "{{ {} }}",
                entries
                    .iter()
                    .map(|(key, value)| format!("{}: {}", key, value))
                    .join(", ")
            ),
            Value::Closure { captured, body } => {
                write!(f, "closure@{}", body)
            }
        }
    }
}

impl From<u64> for Value {
    fn from(value: u64) -> Self {
        Value::Int(value)
    }
}
impl From<String> for Value {
    fn from(value: String) -> Self {
        Value::Text(value)
    }
}
impl From<bool> for Value {
    fn from(it: bool) -> Self {
        Value::Symbol(if it { "True" } else { "False" }.to_string())
    }
}
<|endoftext|>


<commit_before>//! The global state.

use std::sync::{mpsc, Mutex};
use std::collections::HashSet;
use std::mem;
use {rand, hazard};
use garbage::Garbage;

lazy_static! {
    pub static ref STATE: State = State::new();
}

pub fn create_hazard() -> hazard::Writer {
    // Create the hazard.
    let (write, read) = hazard::create();
    // Communicate the new hazard to the global state through the channel.
    STATE.chan.send(Message::NewHazard(read));
    // Return the other half of the hazard.
    write
}

pub fn export_garbage(garbage: Vec<Garbage>) {
    STATE.chan.send(Message::Garbage(garbage));
    tick();
}

pub fn gc() {
    STATE.try_gc();
}

pub fn tick() {
    const GC_PROBABILITY: usize = (!0) / 64;

    if rand::random() < GC_PROBABILITY {
        gc();
    }
}

enum Message {
    Garbage(Vec<Garbage>),
    NewHazard(hazard::Reader),
}

struct State {
    chan: mpsc::Sender<Message>,
    garbo: Mutex<Garbo>,
}

impl State {
    fn new() -> State {
        // Create the message-passing channel.
        let (send, recv) = mpsc::channel();

        // Construct the state from the two halfs of the channel.
        State {
            chan: send,
            garbo: Mutex::new(Garbo {
                chan: recv,
                garbage: Vec::new(),
                hazards: Vec::new(),
            })
        }
    }

    fn try_gc(&self) {
        // Lock the "garbo" (the part of the state needed to GC).
        let garbo = self.garbo.lock();
        // Handle all the messages sent.
        garbo.handle_all();
        // Collect the garbage.
        garbo.gc();
    }
}

struct Garbo {
    chan: mpsc::Receiver<Message>,
    garbage: Vec<Garbage>,
    hazards: Vec<hazard::Reader>,
}

impl State {
    fn handle_all(&mut self) {
        // Pop messages one-by-one and handle them respectively.
        while let Ok(msg) = self.chan.try_recv() {
            self.handle(msg);
        }
    }

    fn handle(&mut self, msg: Message) {
        match msg {
            // Append the garbage bulk to the garbage list.
            Message::Garbage(garbage) => self.garbage.append(garbage),
            // Register the new hazard into the state.
            Message::NewHazard(hazard) => self.hazards.push(hazard),
        }
    }

    fn gc(&mut self) {
        // Create the set which will keep the _active_ hazards.
        let mut active = HashSet::with_capacity(self.hazards.len());

        // Take out the hazards and go over them one-by-one.
        for hazard in mem::replace(self.hazards, Vec::with_capacity(self.hazards.len())) {
            match hazard.get() {
                // The hazard is dead, so the other end (the writer) is not available anymore,
                // hence we can safely destroy it.
                hazard::State::Dead => unsafe { hazard.destroy() },
                // The hazard is free and must thus be put back to the hazard list.
                hazard::State::Free => self.hazards.push(hazard),
                hazard::State::Active(ptr) => {
                    // This hazard is active, hence we insert the pointer it contains in our
                    // "active" set.
                    active.insert(ptr);
                    // Since the hazard is still alive, we must put it back to the hazard list for
                    // future use.
                    self.hazards.push(hazard);
                },
            }
        }

        // Take the garbage and scan it for unused garbage.
        for garbage in mem::replace(self.garbage, Vec::new()) {
            if active.contains(garbage.ptr) {
                // If the garbage is in the set of active pointers, it will be put back to the
                // garbage list.
                self.garbage.push(garbage);
            } else {
                // The garbage is unused and not referenced by any hazard, hence we can safely
                // destroy it.
                unsafe { garbage.destroy(); }
            }
        }
    }
}
<commit_msg>Document `concurrent::global`.<commit_after>//! The global state.

use std::sync::{mpsc, Mutex};
use std::collections::HashSet;
use std::mem;
use {rand, hazard};
use garbage::Garbage;

lazy_static! {
    /// The global state.
    ///
    /// This state is shared between all the threads.
    pub static ref STATE: State = State::new();
}

/// Create a new hazard.
///
/// This creates a new hazard and registers it in the global state. It's secondary, writer part is
/// returned.
pub fn create_hazard() -> hazard::Writer {
    // Create the hazard.
    let (write, read) = hazard::create();
    // Communicate the new hazard to the global state through the channel.
    STATE.chan.send(Message::NewHazard(read));
    // Return the other half of the hazard.
    write
}

/// Export garbage into the global state.
///
/// This adds the garbage, which will eventually be destroyed, to the global state.
pub fn export_garbage(garbage: Vec<Garbage>) {
    // Send the garbage to the message-passing channel of the state.
    STATE.chan.send(Message::Garbage(garbage));
    // Tick the clock, potentially triggering a garbage collection.
    tick();
}

/// Atempt to garbage collect.
///
/// If another garbage collection is currently running, nothing will happen.
pub fn gc() {
    STATE.try_gc();
}

/// Tick the clock.
///
/// This shall be called when new garbage is added, as it will trigger a GC by some probability.
pub fn tick() {
    /// The probabiity of triggering a GC.
    ///
    /// This probability is given such that `0` corresponds to 0 and `!0` corresponds to `1`.
    const GC_PROBABILITY: usize = (!0) / 64;

    // Generate a random number and compare it against the probability.
    if rand::random() <= GC_PROBABILITY {
        // The outfall was to GC.
        gc();
    }
}

/// A message to the global state.
enum Message {
    /// Add new garbage.
    Garbage(Vec<Garbage>),
    /// Add a new hazard.
    NewHazard(hazard::Reader),
}

/// The global state.
///
/// The global state is shared between all threads and keeps track of the garbage and the active
/// hazards.
///
/// It is divided into two parts: The channel and the garbo. The channel buffers messages, which
/// will eventually be executed at garbo, which holds all the data structures and is protected by a
/// mutex. The garbo holds the other end to the channel.
struct State {
    /// The message-passing channel.
    chan: mpsc::Sender<Message>,
    /// The garbo part of the state.
    garbo: Mutex<Garbo>,
}

impl State {
    /// Initialize a new state.
    fn new() -> State {
        // Create the message-passing channel.
        let (send, recv) = mpsc::channel();

        // Construct the state from the two halfs of the channel.
        State {
            chan: send,
            garbo: Mutex::new(Garbo {
                chan: recv,
                garbage: Vec::new(),
                hazards: Vec::new(),
            })
        }
    }

    /// Try to collect the garbage.
    ///
    /// This will handle all of the messages in the channel and then attempt at collect the
    /// garbage. If another thread is currently collecting garbage, it will be equivalent to NOP.
    ///
    /// Garbage collection works by scanning the hazards and dropping all the garbage which is not
    /// currently active in the hazards.
    fn try_gc(&self) {
        // Lock the "garbo" (the part of the state needed to GC).
        if let Ok(garbo) = self.garbo.try_lock() {
            // Handle all the messages sent.
            garbo.handle_all();
            // Collect the garbage.
            garbo.gc();
        }
    }
}

/// The garbo part of the state.
///
/// This part is supposed to act like the garbage collecting part. It handles hazards, garbage, and
/// the receiving point of the message-passing channel.
struct Garbo {
    /// The channel of messages.
    chan: mpsc::Receiver<Message>,
    /// The to-be-destroyed garbage.
    garbage: Vec<Garbage>,
    /// The current hazards.
    hazards: Vec<hazard::Reader>,
}

impl Garbo {
    /// Handle a given message.
    ///
    /// "Handle" in this case refers to applying the operation defined by the message to the state,
    /// effectually executing the instruction of the message.
    fn handle(&mut self, msg: Message) {
        match msg {
            // Append the garbage bulk to the garbage list.
            Message::Garbage(garbage) => self.garbage.append(garbage),
            // Register the new hazard into the state.
            Message::NewHazard(hazard) => self.hazards.push(hazard),
        }
    }

    /// Receive and handle all the messages.
    fn handle_all(&mut self) {
        // Pop messages one-by-one and handle them respectively.
        while let Ok(msg) = self.chan.try_recv() {
            self.handle(msg);
        }
    }

    /// Garbage collect all unused garbage.
    fn gc(&mut self) {
        // Create the set which will keep the _active_ hazards.
        let mut active = HashSet::with_capacity(self.hazards.len());

        // Take out the hazards and go over them one-by-one.
        for hazard in mem::replace(self.hazards, Vec::with_capacity(self.hazards.len())) {
            match hazard.get() {
                // The hazard is dead, so the other end (the writer) is not available anymore,
                // hence we can safely destroy it.
                hazard::State::Dead => unsafe { hazard.destroy() },
                // The hazard is free and must thus be put back to the hazard list.
                hazard::State::Free => self.hazards.push(hazard),
                hazard::State::Active(ptr) => {
                    // This hazard is active, hence we insert the pointer it contains in our
                    // "active" set.
                    active.insert(ptr);
                    // Since the hazard is still alive, we must put it back to the hazard list for
                    // future use.
                    self.hazards.push(hazard);
                },
            }
        }

        // Take the garbage and scan it for unused garbage.
        for garbage in mem::replace(self.garbage, Vec::new()) {
            if active.contains(garbage.ptr) {
                // If the garbage is in the set of active pointers, it will be put back to the
                // garbage list.
                self.garbage.push(garbage);
            } else {
                // The garbage is unused and not referenced by any hazard, hence we can safely
                // destroy it.
                unsafe { garbage.destroy(); }
            }
        }
    }
}
<|endoftext|>


<commit_before>#[test]
fn it_works() {
}
<commit_msg>Add modules in lib.rs<commit_after>extern crate toml;

pub mod content;
pub mod entry;
pub mod error;
pub mod header;

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::cmp;
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};

use model::ttable::{Score, TTable};
use model::{Board, Move, Outcome};

const NEG_INFINITY: i16 = -0x7000;
const LOSE: i16 = -0x4000;
// Small contempt factor to discourage draws
const DRAW: i16 = 1;
const ASPIRATION_WIDTH: i16 = 51;

pub struct AI {
    status: Status,
    ttable: Arc<Mutex<TTable>>,
}

enum Status {
    Idle,
    // Either the AI thread is running, or there is a move waiting to be received
    Thinking {
        move_recv: Receiver<Move>,
        // We store and load this atomic with Ordering::Relaxed. It *should* be fine because it
        // doesn't interact with any other atomics--all we want to do is tell the AI thread to stop
        // searching for a move
        stop_signal: Arc<AtomicBool>,
        handle: JoinHandle<()>,
    },
}

impl AI {
    pub fn new() -> Self {
        Self {
            status: Status::Idle,
            ttable: Arc::new(Mutex::new(TTable::new())),
        }
    }

    pub fn is_idle(&self) -> bool {
        match self.status {
            Status::Idle => true,
            Status::Thinking { .. } => false,
        }
    }

    pub fn stop(&mut self) {
        if let Status::Thinking {
            ref stop_signal, ..
        } = self.status
        {
            stop_signal.store(true, Ordering::Relaxed);
        }
        // Unconditionally assign because without NLL, we can't put this in the if let block above
        self.status = Status::Idle;
    }

    pub fn try_recv(&mut self) -> Option<Move> {
        use self::TryRecvError::*;

        let result;
        match self.status {
            Status::Idle => result = None,
            Status::Thinking { ref move_recv, .. } => match move_recv.try_recv() {
                Ok(mv) => result = Some(mv),
                Err(Empty) => result = None,
                Err(Disconnected) => panic!("Tried to receive move from disconnected sender"),
            },
        }

        // We can't set status in the Ok(mv) arm above without NLL
        if result.is_some() {
            self.status = Status::Idle;
        }
        result
    }

    pub fn think(&mut self, board: Board, board_list: Vec<Board>, depth: u8) {
        assert_ne!(depth, 0);

        let prev_status = mem::replace(&mut self.status, Status::Idle);

        let (move_sender, move_recv) = mpsc::channel();
        let stop_signal = Arc::new(AtomicBool::new(false));
        let stop_signal_clone = stop_signal.clone();

        let ttable_mutex = self.ttable.clone();

        let handle = thread::spawn(move || {
            if let Status::Thinking {
                stop_signal,
                handle,
                ..
            } = prev_status
            {
                stop_signal.store(true, Ordering::Relaxed);
                handle
                    .join()
                    .expect("Old AI thread panicked when new AI thread joined on it");
            }

            // If the previous AI thread was sent the stop signal, but hasn't received it yet, we
            // will block here until it finishes. We won't have joined on its handle above because
            // stop throws away its Status enum.
            let mut ttable = match ttable_mutex.lock() {
                Ok(table) => table,
                Err(_) => panic!("Transposition table mutex is poisoned"),
            };

            if let SearchResult::Move(mv) =
                search_root(depth, board, board_list, &mut ttable, &stop_signal_clone)
            {
                move_sender.send(mv).expect("AI failed to send Move");
            }
        });

        self.status = Status::Thinking {
            move_recv,
            stop_signal,
            handle,
        };
    }
}

enum SearchResult {
    Move(Move),
    Stopped,
}

fn search_root(
    depth: u8,
    board: Board,
    board_list: Vec<Board>,
    ttable: &mut TTable,
    stop_signal: &Arc<AtomicBool>,
) -> SearchResult {
    ttable.inc_age();

    // Only take positions after the last irreversible move
    let mut board_list: Vec<_> = board_list
        .into_iter()
        .rev()
        .take_while(|b| b.vitals == board.vitals)
        .collect();
    board_list.reverse();

    let mut moves: Vec<(Move, i16)> = board
        .generate_moves()
        .map(|mv| (mv, NEG_INFINITY))
        .collect();

    if moves.is_empty() {
        panic!("AI has no moves");
    }

    let mut pv = None;
    let mut iter_score = evaluate(&board);
    for depth in 0..depth {
        if stop_signal.load(Ordering::Relaxed) {
            return SearchResult::Stopped;
        }

        // Aspiration window search loop
        let mut asp_width = ASPIRATION_WIDTH;
        loop {
            let mut max_score = iter_score - asp_width;
            for pair in &mut moves {
                let mut new_board = board;
                new_board.apply_move(&pair.0);

                let mut new_pv = vec![];

                let score = -alphabeta_negamax(
                    &new_board,
                    &mut board_list,
                    &mut new_pv,
                    -(iter_score + asp_width),
                    -max_score,
                    depth,
                    ttable,
                );

                if score > max_score {
                    max_score = score;
                    pv = Some(new_pv);
                }
                pair.1 = score;
            }

            if max_score == iter_score + asp_width || max_score == iter_score - asp_width {
                // True score lies outside the window, so we search this position again
                asp_width *= 2;
            } else {
                break;
            }
        }

        moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
        iter_score = moves[0].1;

        println!("\nDepth {}: {:>6}", depth, moves[0].1);
        if let Some(ref mut pv) = pv {
            pv.push(moves[0].0);
            for mv in pv.iter().rev() {
                println!("    {}", mv);
            }
        }
    }
    println!("\n---------------------");

    SearchResult::Move(moves[0].0)
}

fn alphabeta_negamax(
    board: &Board,
    // This list does not include the current board
    mut board_list: &mut Vec<Board>,
    pv: &mut Vec<Move>,
    mut alpha: i16,
    mut beta: i16,
    depth: u8,
    ttable: &mut TTable,
) -> i16 {
    let mut set_pv = move |score, new_pv| {
        if score > alpha && score < beta {
            *pv = new_pv;
        }
    };
    let set_ttable = |ttable: &mut TTable, score| {
        ttable.set(board.zobrist, score, depth as i8);
    };

    use self::Outcome::*;
    match board.outcome() {
        DrawStalemate | DrawInsufficientMaterial => {
            // This is safe to do because Board does not detect draws by threefold repetition
            set_ttable(ttable, Score::Exact(DRAW));
            set_pv(DRAW, vec![]);
            return DRAW;
        }
        Win(color) => {
            assert_ne!(color, board.turn);
            // Weight score by depth to encourage shorter wins. The shorter the win, the greater
            // `depth` will be, and so the larger the score will be. This also encourages the AI to
            // prolong a loss.
            let score = LOSE - i16::from(depth);
            set_ttable(ttable, Score::Exact(score));
            set_pv(score, vec![]);
            return score;
        }
        InProgress => {}
        DrawThreefoldRepetition => unreachable!(),
    }

    if board_list.len() >= 8 && board_list.iter().filter(|&&b| b == *board).count() >= 2 {
        set_pv(DRAW, vec![]);
        return DRAW;
    }

    if depth == 0 {
        let score = quiescence_search(board, alpha, beta, depth as i8, ttable);
        set_pv(score, vec![]);
        return score;
    }

    {
        match ttable.get(board.zobrist, depth as i8) {
            Some(Score::Exact(score)) => {
                // This will cut the PV short
                // TODO: Store the best move in the table and get the PV from that?
                set_pv(score, vec![]);
                return score;
            }
            Some(Score::Beta(score)) => {
                if score >= beta {
                    return score;
                }
                beta = score;
            }
            None => {}
        }
    }

    let mut best_score = NEG_INFINITY;
    let mut best_move = None;

    let mut new_pv = vec![];
    for mv in board.generate_moves() {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        board_list.push(*board);
        let score = -alphabeta_negamax(
            &new_board,
            &mut board_list,
            &mut new_pv,
            -beta,
            -alpha,
            depth - 1,
            ttable,
        );
        board_list.pop();

        best_score = cmp::max(score, best_score);

        if score >= beta {
            set_ttable(ttable, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
            best_move = Some(mv);
        }
    }
    set_ttable(ttable, Score::Exact(best_score));
    if let Some(mv) = best_move {
        new_pv.push(mv);
        set_pv(alpha, new_pv);
    }
    alpha
}

fn quiescence_search(
    board: &Board,
    mut alpha: i16,
    mut beta: i16,
    depth: i8,
    ttable: &mut TTable,
) -> i16 {
    let stand_pat = evaluate(board);
    if stand_pat >= beta {
        return beta;
    } else if stand_pat + 200 < alpha {
        // Delta pruning: we bet that no capture move will raise alpha by more than 200 centipieces
        // We don't switch this off in the endgame because a draw by insufficient material is very
        // unlikely (both sides must be unable to exchange and each have only 1 piece left)
        return alpha;
    } else if alpha < stand_pat {
        alpha = stand_pat;
    }

    match ttable.get(board.zobrist, depth) {
        Some(Score::Exact(score)) => {
            return score;
        }
        Some(Score::Beta(score)) => {
            if score >= beta {
                return score;
            }
            beta = score;
        }
        None => {}
    };

    let set_ttable = |ttable: &mut TTable, score| {
        ttable.set(board.zobrist, score, depth);
    };

    for mv in board.generate_captures() {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        let score = -quiescence_search(&new_board, -beta, -alpha, depth - 1, ttable);

        if score >= beta {
            set_ttable(ttable, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
        }
    }
    set_ttable(ttable, Score::Exact(alpha));
    alpha
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}
<commit_msg>Check stop signal before sending move<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::cmp;
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};

use model::ttable::{Score, TTable};
use model::{Board, Move, Outcome};

const NEG_INFINITY: i16 = -0x7000;
const LOSE: i16 = -0x4000;
// Small contempt factor to discourage draws
const DRAW: i16 = 1;
const ASPIRATION_WIDTH: i16 = 51;

pub struct AI {
    status: Status,
    ttable: Arc<Mutex<TTable>>,
}

enum Status {
    Idle,
    // Either the AI thread is running, or there is a move waiting to be received
    Thinking {
        move_recv: Receiver<Move>,
        // We store and load this atomic with Ordering::Relaxed. It *should* be fine because it
        // doesn't interact with any other atomics--all we want to do is tell the AI thread to stop
        // searching for a move
        stop_signal: Arc<AtomicBool>,
        handle: JoinHandle<()>,
    },
}

impl AI {
    pub fn new() -> Self {
        Self {
            status: Status::Idle,
            ttable: Arc::new(Mutex::new(TTable::new())),
        }
    }

    pub fn is_idle(&self) -> bool {
        match self.status {
            Status::Idle => true,
            Status::Thinking { .. } => false,
        }
    }

    pub fn stop(&mut self) {
        if let Status::Thinking {
            ref stop_signal, ..
        } = self.status
        {
            stop_signal.store(true, Ordering::Relaxed);
        }
        // Unconditionally assign because without NLL, we can't put this in the if let block above
        self.status = Status::Idle;
    }

    pub fn try_recv(&mut self) -> Option<Move> {
        use self::TryRecvError::*;

        let result;
        match self.status {
            Status::Idle => result = None,
            Status::Thinking { ref move_recv, .. } => match move_recv.try_recv() {
                Ok(mv) => result = Some(mv),
                Err(Empty) => result = None,
                Err(Disconnected) => panic!("Tried to receive move from disconnected sender"),
            },
        }

        // We can't set status in the Ok(mv) arm above without NLL
        if result.is_some() {
            self.status = Status::Idle;
        }
        result
    }

    pub fn think(&mut self, board: Board, board_list: Vec<Board>, depth: u8) {
        assert_ne!(depth, 0);

        let prev_status = mem::replace(&mut self.status, Status::Idle);

        let (move_sender, move_recv) = mpsc::channel();
        let stop_signal = Arc::new(AtomicBool::new(false));
        let stop_signal_clone = stop_signal.clone();

        let ttable_mutex = self.ttable.clone();

        let handle = thread::spawn(move || {
            if let Status::Thinking {
                stop_signal,
                handle,
                ..
            } = prev_status
            {
                stop_signal.store(true, Ordering::Relaxed);
                handle
                    .join()
                    .expect("Old AI thread panicked when new AI thread joined on it");
            }

            // If the previous AI thread was sent the stop signal, but hasn't received it yet, we
            // will block here until it finishes. We won't have joined on its handle above because
            // stop throws away its Status enum.
            let mut ttable = match ttable_mutex.lock() {
                Ok(table) => table,
                Err(_) => panic!("Transposition table mutex is poisoned"),
            };

            if let SearchResult::Move(mv) =
                search_root(depth, board, board_list, &mut ttable, &stop_signal_clone)
            {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }
                move_sender.send(mv).expect("AI failed to send Move");
            }
        });

        self.status = Status::Thinking {
            move_recv,
            stop_signal,
            handle,
        };
    }
}

enum SearchResult {
    Move(Move),
    Stopped,
}

fn search_root(
    depth: u8,
    board: Board,
    board_list: Vec<Board>,
    ttable: &mut TTable,
    stop_signal: &Arc<AtomicBool>,
) -> SearchResult {
    ttable.inc_age();

    // Only take positions after the last irreversible move
    let mut board_list: Vec<_> = board_list
        .into_iter()
        .rev()
        .take_while(|b| b.vitals == board.vitals)
        .collect();
    board_list.reverse();

    let mut moves: Vec<(Move, i16)> = board
        .generate_moves()
        .map(|mv| (mv, NEG_INFINITY))
        .collect();

    if moves.is_empty() {
        panic!("AI has no moves");
    }

    let mut pv = None;
    let mut iter_score = evaluate(&board);
    for depth in 0..depth {
        if stop_signal.load(Ordering::Relaxed) {
            return SearchResult::Stopped;
        }

        // Aspiration window search loop
        let mut asp_width = ASPIRATION_WIDTH;
        loop {
            let mut max_score = iter_score - asp_width;
            for pair in &mut moves {
                let mut new_board = board;
                new_board.apply_move(&pair.0);

                let mut new_pv = vec![];

                let score = -alphabeta_negamax(
                    &new_board,
                    &mut board_list,
                    &mut new_pv,
                    -(iter_score + asp_width),
                    -max_score,
                    depth,
                    ttable,
                );

                if score > max_score {
                    max_score = score;
                    pv = Some(new_pv);
                }
                pair.1 = score;
            }

            if max_score == iter_score + asp_width || max_score == iter_score - asp_width {
                // True score lies outside the window, so we search this position again
                asp_width *= 2;
            } else {
                break;
            }
        }

        moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
        iter_score = moves[0].1;

        println!("\nDepth {}: {:>6}", depth, moves[0].1);
        if let Some(ref mut pv) = pv {
            pv.push(moves[0].0);
            for mv in pv.iter().rev() {
                println!("    {}", mv);
            }
        }
    }
    println!("\n---------------------");

    SearchResult::Move(moves[0].0)
}

fn alphabeta_negamax(
    board: &Board,
    // This list does not include the current board
    mut board_list: &mut Vec<Board>,
    pv: &mut Vec<Move>,
    mut alpha: i16,
    mut beta: i16,
    depth: u8,
    ttable: &mut TTable,
) -> i16 {
    let mut set_pv = move |score, new_pv| {
        if score > alpha && score < beta {
            *pv = new_pv;
        }
    };
    let set_ttable = |ttable: &mut TTable, score| {
        ttable.set(board.zobrist, score, depth as i8);
    };

    use self::Outcome::*;
    match board.outcome() {
        DrawStalemate | DrawInsufficientMaterial => {
            // This is safe to do because Board does not detect draws by threefold repetition
            set_ttable(ttable, Score::Exact(DRAW));
            set_pv(DRAW, vec![]);
            return DRAW;
        }
        Win(color) => {
            assert_ne!(color, board.turn);
            // Weight score by depth to encourage shorter wins. The shorter the win, the greater
            // `depth` will be, and so the larger the score will be. This also encourages the AI to
            // prolong a loss.
            let score = LOSE - i16::from(depth);
            set_ttable(ttable, Score::Exact(score));
            set_pv(score, vec![]);
            return score;
        }
        InProgress => {}
        DrawThreefoldRepetition => unreachable!(),
    }

    if board_list.len() >= 8 && board_list.iter().filter(|&&b| b == *board).count() >= 2 {
        set_pv(DRAW, vec![]);
        return DRAW;
    }

    if depth == 0 {
        let score = quiescence_search(board, alpha, beta, depth as i8, ttable);
        set_pv(score, vec![]);
        return score;
    }

    {
        match ttable.get(board.zobrist, depth as i8) {
            Some(Score::Exact(score)) => {
                // This will cut the PV short
                // TODO: Store the best move in the table and get the PV from that?
                set_pv(score, vec![]);
                return score;
            }
            Some(Score::Beta(score)) => {
                if score >= beta {
                    return score;
                }
                beta = score;
            }
            None => {}
        }
    }

    let mut best_score = NEG_INFINITY;
    let mut best_move = None;

    let mut new_pv = vec![];
    for mv in board.generate_moves() {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        board_list.push(*board);
        let score = -alphabeta_negamax(
            &new_board,
            &mut board_list,
            &mut new_pv,
            -beta,
            -alpha,
            depth - 1,
            ttable,
        );
        board_list.pop();

        best_score = cmp::max(score, best_score);

        if score >= beta {
            set_ttable(ttable, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
            best_move = Some(mv);
        }
    }
    set_ttable(ttable, Score::Exact(best_score));
    if let Some(mv) = best_move {
        new_pv.push(mv);
        set_pv(alpha, new_pv);
    }
    alpha
}

fn quiescence_search(
    board: &Board,
    mut alpha: i16,
    mut beta: i16,
    depth: i8,
    ttable: &mut TTable,
) -> i16 {
    let stand_pat = evaluate(board);
    if stand_pat >= beta {
        return beta;
    } else if stand_pat + 200 < alpha {
        // Delta pruning: we bet that no capture move will raise alpha by more than 200 centipieces
        // We don't switch this off in the endgame because a draw by insufficient material is very
        // unlikely (both sides must be unable to exchange and each have only 1 piece left)
        return alpha;
    } else if alpha < stand_pat {
        alpha = stand_pat;
    }

    match ttable.get(board.zobrist, depth) {
        Some(Score::Exact(score)) => {
            return score;
        }
        Some(Score::Beta(score)) => {
            if score >= beta {
                return score;
            }
            beta = score;
        }
        None => {}
    };

    let set_ttable = |ttable: &mut TTable, score| {
        ttable.set(board.zobrist, score, depth);
    };

    for mv in board.generate_captures() {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        let score = -quiescence_search(&new_board, -beta, -alpha, depth - 1, ttable);

        if score >= beta {
            set_ttable(ttable, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
        }
    }
    set_ttable(ttable, Score::Exact(alpha));
    alpha
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}
<|endoftext|>


<commit_before>use voodoo::window::{Point};

use map_view::MapView;
use level::{self, Level};
use program::{Ability, Program, ProgramRef, Team};

enum AIChoice {
    Ability {
        ability: Ability,
        target: ProgramRef,
    },
    Move(Point),
}

pub fn ai_tick(level: &Level, map: &mut MapView) {
    for program in level.programs.iter() {
        if program.borrow().team != Team::Enemy {
            continue;
        }
        let position = { program.borrow().position };
        let Point { x, y } = position;
        let abilities = { program.borrow().abilities.clone() };
        let mut choices = vec![];

        for (_, ability) in abilities {
            for reachable in ability.reachable_tiles(position) {
                match level.contents_of(reachable) {
                    level::CellContents::Program(target) => {
                        if target.borrow().team == Team::Player {
                            choices.push((100, AIChoice::Ability {
                                ability: ability,
                                target: target,
                            }));
                        }
                    }
                    _ => {},
                }
            }
        }

        let east = Point::new(x + 1, y);
        if level.passable(east) {
            choices.push((50, AIChoice::Move(east)));
        }
        let west = Point::new(x - 1, y);
        if level.passable(west) {
            choices.push((50, AIChoice::Move(west)));
        }
        let north = Point::new(x, y - 1);
        if level.passable(north) {
            choices.push((50, AIChoice::Move(north)));
        }
        let south = Point::new(x, y + 1);
        if level.passable(south) {
            choices.push((50, AIChoice::Move(south)));
        }

        choices.sort_by(|&(s1, _), &(s2, _)| { s2.cmp(&s1) });
        if let Some(&(_, ref choice)) = choices.first() {
            match choice {
                &AIChoice::Ability { ability, ref target } => {
                    ability.apply(&mut target.borrow_mut());
                }
                &AIChoice::Move(point) => {
                    program.borrow_mut().move_to(point);
                }
            }
        }
    }
}
<commit_msg>feat: only allow AI to use abilities once per turn<commit_after>use voodoo::window::{Point};

use map_view::MapView;
use level::{self, Level};
use program::{Ability, Program, ProgramRef, Team};

enum AIChoice {
    Ability {
        ability: Ability,
        target: ProgramRef,
    },
    Move(Point),
}

pub fn ai_tick(level: &Level, map: &mut MapView) {
    for program in level.programs.iter() {
        if program.borrow().team != Team::Enemy {
            continue;
        }
        let position = { program.borrow().position };
        let Point { x, y } = position;
        let abilities = { program.borrow().abilities.clone() };
        let ability_used = { program.borrow().turn_state.ability_used };
        let mut choices = vec![];

        if !ability_used {
            for (_, ability) in abilities {
                for reachable in ability.reachable_tiles(position) {
                    match level.contents_of(reachable) {
                        level::CellContents::Program(target) => {
                            if target.borrow().team == Team::Player {
                                choices.push((100, AIChoice::Ability {
                                    ability: ability,
                                    target: target,
                                }));
                            }
                        }
                        _ => {},
                    }
                }
            }
        }

        let east = Point::new(x + 1, y);
        if level.passable(east) {
            choices.push((50, AIChoice::Move(east)));
        }
        let west = Point::new(x - 1, y);
        if level.passable(west) {
            choices.push((50, AIChoice::Move(west)));
        }
        let north = Point::new(x, y - 1);
        if level.passable(north) {
            choices.push((50, AIChoice::Move(north)));
        }
        let south = Point::new(x, y + 1);
        if level.passable(south) {
            choices.push((50, AIChoice::Move(south)));
        }

        choices.sort_by(|&(s1, _), &(s2, _)| { s2.cmp(&s1) });
        if let Some(&(_, ref choice)) = choices.first() {
            match choice {
                &AIChoice::Ability { ability, ref target } => {
                    program.borrow_mut().turn_state.ability_used = true;
                    ability.apply(&mut target.borrow_mut());
                }
                &AIChoice::Move(point) => {
                    program.borrow_mut().move_to(point);
                }
            }
        }
    }
}
<|endoftext|>