// run-pass

#![allow(unused_imports)]
#![deny(unused_assignments)]

use std::mem;
use std::ops::{
    AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, Index, MulAssign, RemAssign,
    ShlAssign, ShrAssign, SubAssign,
};

#[derive(Debug, PartialEq)]
struct Int(i32);

struct Slice([i32]);

impl Slice {
    fn new(slice: &mut [i32]) -> &mut Slice {
        unsafe {
            mem::transmute(slice)
        }
    }
}

struct View<'a>(&'a mut [i32]);

fn main() {
    let mut x = Int(1);

    x += Int(2);
    assert_eq!(x, Int(0b11));

    x &= Int(0b01);
    assert_eq!(x, Int(0b01));

    x |= Int(0b10);
    assert_eq!(x, Int(0b11));

    x ^= Int(0b01);
    assert_eq!(x, Int(0b10));

    x /= Int(2);
    assert_eq!(x, Int(1));

    x *= Int(3);
    assert_eq!(x, Int(3));

    x %= Int(2);
    assert_eq!(x, Int(1));

    // overloaded RHS
    x <<= 1u8;
    assert_eq!(x, Int(2));

    x <<= 1u16;
    assert_eq!(x, Int(4));

    x >>= 1u8;
    assert_eq!(x, Int(2));

    x >>= 1u16;
    assert_eq!(x, Int(1));

    x -= Int(1);
    assert_eq!(x, Int(0));

    // indexed LHS
    let mut v = vec![Int(1), Int(2)];
    v[0] += Int(2);
    assert_eq!(v[0], Int(3));

    // unsized RHS
    let mut array = [0, 1, 2];
    *Slice::new(&mut array) += 1;
    assert_eq!(array[0], 1);
    assert_eq!(array[1], 2);
    assert_eq!(array[2], 3);

    // sized indirection
    // check that this does *not* trigger the unused_assignments lint
    let mut array = [0, 1, 2];
    let mut view = View(&mut array);
    view += 1;
}

impl AddAssign for Int {
    fn add_assign(&mut self, rhs: Int) {
        self.0 += rhs.0;
    }
}

impl BitAndAssign for Int {
    fn bitand_assign(&mut self, rhs: Int) {
        self.0 &= rhs.0;
    }
}

impl BitOrAssign for Int {
    fn bitor_assign(&mut self, rhs: Int) {
        self.0 |= rhs.0;
    }
}

impl BitXorAssign for Int {
    fn bitxor_assign(&mut self, rhs: Int) {
        self.0 ^= rhs.0;
    }
}

impl DivAssign for Int {
    fn div_assign(&mut self, rhs: Int) {
        self.0 /= rhs.0;
    }
}

impl MulAssign for Int {
    fn mul_assign(&mut self, rhs: Int) {
        self.0 *= rhs.0;
    }
}

impl RemAssign for Int {
    fn rem_assign(&mut self, rhs: Int) {
        self.0 %= rhs.0;
    }
}

impl ShlAssign<u8> for Int {
    fn shl_assign(&mut self, rhs: u8) {
        self.0 <<= rhs;
    }
}

impl ShlAssign<u16> for Int {
    fn shl_assign(&mut self, rhs: u16) {
        self.0 <<= rhs;
    }
}

impl ShrAssign<u8> for Int {
    fn shr_assign(&mut self, rhs: u8) {
        self.0 >>= rhs;
    }
}

impl ShrAssign<u16> for Int {
    fn shr_assign(&mut self, rhs: u16) {
        self.0 >>= rhs;
    }
}

impl SubAssign for Int {
    fn sub_assign(&mut self, rhs: Int) {
        self.0 -= rhs.0;
    }
}

impl AddAssign<i32> for Slice {
    fn add_assign(&mut self, rhs: i32) {
        for lhs in &mut self.0 {
            *lhs += rhs;
        }
    }
}

impl<'a> AddAssign<i32> for View<'a> {
    fn add_assign(&mut self, rhs: i32) {
        for lhs in self.0.iter_mut() {
            *lhs += rhs;
        }
    }
}
