<commit_before>use super::Columns;

use std::cell::RefCell;
use std::f64::consts::{PI, SQRT_2};
use std::ops::{Index, IndexMut};

struct ColumnsMut<'a, T: 'a> {
    data: &'a mut [T],
    rowstride: usize,
    curr: usize,
}

impl<'a, T: 'a> ColumnsMut<'a, T> {
    #[inline(always)]
    fn from_slice(data: &'a mut [T], rowstride: usize) -> Self {
        ColumnsMut {
            data: data,
            rowstride: rowstride,
            curr: 0,
        }
    }
}

impl<'a, T: 'a> Iterator for ColumnsMut<'a, T> {
    type Item = ColumnMut<'a, T>;
    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        if self.curr < self.rowstride {
           let data = unsafe { &mut *(&mut self.data[self.curr..] as *mut [T]) };
            self.curr += 1;
            Some(ColumnMut {
                data: data,
                rowstride: self.rowstride,
            })
        } else {
            None
        }
    }
}

struct ColumnMut<'a, T: 'a> {
    data: &'a mut [T],
    rowstride: usize,
}

impl<'a, T: 'a> Index<usize> for ColumnMut<'a, T> {
    type Output = T;
    #[inline(always)]
    fn index(&self, idx: usize) -> &T {
       &self.data[idx * self.rowstride]
    }
}

impl<'a, T: 'a> IndexMut<usize> for ColumnMut<'a, T> {
    #[inline(always)]
    fn index_mut(&mut self, idx: usize) -> &mut T {
       &mut self.data[idx * self.rowstride]
    }
}

thread_local! {
    static PRECOMPUTED_MATRIX: RefCell<Vec<f64>> = RefCell::new(Vec::new());
}

/// Precompute the DCT matrix for a given hash size and memoize it in thread-local
/// storage.
///
///
/// If a precomputed matrix was already stored, even of the same length, it will be overwritten.
///
/// This can produce a significant runtime savings (an order of magnitude on the author's machine)
/// when performing multiple hashing runs with the same hash size, as compared to not performing
/// this step.
///
/// ## Note
/// This only affects the built-in DCT hash (`HashType::DCT`). It also includes
/// the hash size multiplier applied by the DCT hash algorithm.
///
/// ## Note: Thread-Local
/// Because this uses thread-local storage, this will need to be called on every thread
/// that will be using the DCT hash for the runtime benefit. You can also
/// have precomputed matrices of different sizes for each thread.
pub fn precompute_dct_matrix(size: u32) {
    // The DCT hash uses a hash size larger than the user provided, so we have to
    // precompute a matrix of the right size
    let size = size * ::DCT_HASH_SIZE_MULTIPLIER;
    precomp_exact(size);
}

/// Precompute a DCT matrix of the exact given size.
pub fn precomp_exact(size: u32) {
    PRECOMPUTED_MATRIX.with(|matrix| precompute_matrix(size as usize, &mut matrix.borrow_mut()));
}

#[cfg(feature = "bench")]
pub fn clear_precomputed_matrix() {
    PRECOMPUTED_MATRIX.with(|matrix| matrix.borrow_mut().clear());
}

fn precompute_matrix(size: usize, matrix: &mut Vec<f64>) {
    matrix.resize(size * size, 0.0);

    for i in 0 .. size {
        for j in 0 .. size {
            matrix[i * size + j] = (PI * i as f64 * (2 * j + 1) as f64 / (2 * size) as f64).cos();
        }
    }
}

fn with_precomputed_matrix<F>(size: usize, with_fn: F) -> bool
where F: FnOnce(&[f64]) {
    PRECOMPUTED_MATRIX.with(|matrix| {
        let matrix = matrix.borrow();

        if matrix.len() == size * size {
            with_fn(&matrix);
            true
        } else {
            false
        }
    })
}

pub fn dct_1d<I: Index<usize, Output=f64> + ?Sized, O: IndexMut<usize, Output=f64> + ?Sized>(input: &I, output: &mut O, len: usize) {
    if with_precomputed_matrix(len, |matrix| dct_1d_precomputed(input, output, len, matrix)) {
        return;
    }

    for i in 0 .. len {
        let mut z = 0.0;

        for j in 0 .. len {
            z += input[j] * (
                PI * i as f64 * (2 * j + 1) as f64
                / (2 * len) as f64
            ).cos();
        }

        if i == 0 {
            z *= 1.0 / SQRT_2;
        }

        output[i] = z / 2.0;
    }
}

fn dct_1d_precomputed<I: ?Sized, O: ?Sized>(input: &I, output: &mut O, len: usize, matrix: &[f64])
where I: Index<usize, Output=f64>, O: IndexMut<usize, Output=f64> {
    for i in 0 .. len {
        let mut z = 0.0;

        for j in 0 .. len {
            z += input[j] * matrix[i * len + j];
        }

        if i == 0 {
            z *= 1.0 / SQRT_2;
        }

        output[i] = z / 2.0;
    }
}

/// Perform a 2D DCT on a 1D-packed vector with a given rowstride.
///
/// E.g. a vector of length 9 with a rowstride of 3 will be processed as a 3x3 matrix.
///
/// Returns a vector of the same size packed in the same way.
pub fn dct_2d(packed_2d: &[f64], rowstride: usize) -> Vec<f64> {
    assert_eq!(packed_2d.len() % rowstride, 0);

    let mut scratch = Vec::with_capacity(packed_2d.len() * 2);
    unsafe { scratch.set_len(packed_2d.len() * 2); }

    {
        let (col_pass, row_pass) = scratch.split_at_mut(packed_2d.len());

        for (row_in, row_out) in packed_2d.chunks(rowstride)
                .zip(row_pass.chunks_mut(rowstride)) {
            dct_1d(row_in, row_out, rowstride);
        }

        for (col_in, mut col_out) in Columns::from_slice(row_pass, rowstride)
                .zip(ColumnsMut::from_slice(col_pass, rowstride)) {
            dct_1d(&col_in, &mut col_out, rowstride);
        }
    }

    scratch.truncate(packed_2d.len());
    scratch
}

/*
#[cfg(feature = "simd")]
mod dct_simd {
    use simdty::f64x2;

    use std::f64::consts::{PI, SQRT_2};

    macro_rules! valx2 ( ($val:expr) => ( ::simdty::f64x2($val, $val) ) );

    const PI: f64x2 = valx2!(PI);
    const ONE_DIV_SQRT_2: f64x2 = valx2!(1 / SQRT_2);
    const SQRT_2: f64x2 = valx2!(SQRT_2);

    pub dct_rows(vals: &[Vec<f64>]) -> Vec<Vec<f64>> {
        let mut out = Vec::with_capacity(vals.len());

        for pair in vals.iter().chunks(2) {
            if pair.len() == 2 {
                let vals = pair[0].iter().cloned().zip(pair[1].iter().cloned())
                    .map(f64x2)
                    .collect();

                dct_1dx2(vals);

        }
    }

    fn dct_1dx2(vec: Vec<f64x2>) -> Vec<f64x2> {
        let mut out = Vec::with_capacity(vec.len());

        for u in 0 .. vec.len() {
            let mut z = valx2!(0.0);

            for x in 0 .. vec.len() {
                z += vec[x] * cos_approx(
                    PI * valx2!(
                        u as f64 * (2 * x + 1) as f64
                            / (2 * vec.len()) as f64
                    )
                );
            }

            if u == 0 {
                z *= ONE_DIV_SQRT_2;
            }

            out.insert(u, z / valx2!(2.0));
        }

        out
    }

    fn cos_approx(x2: f64x2) -> f64x2 {
        #[inline(always)]
        fn powi(val: f64x2, pow: i32) -> f64x2 {
            unsafe { llvmint::powi_v2f64(val, pow) }
        }

        let x2 = powi(val, 2);
        let x4 = powi(val, 4);
        let x6 = powi(val, 6);
        let x8 = powi(val, 8);

        valx2!(1.0) - (x2 / valx2!(2.0)) + (x4 / valx2!(24.0))
            - (x6 / valx2!(720.0)) + (x8 / valx2!(40320.0))
    }
}
*/

<commit_msg>Improve documentation on hash sizes (2)<commit_after>use super::Columns;

use std::cell::RefCell;
use std::f64::consts::{PI, SQRT_2};
use std::ops::{Index, IndexMut};

struct ColumnsMut<'a, T: 'a> {
    data: &'a mut [T],
    rowstride: usize,
    curr: usize,
}

impl<'a, T: 'a> ColumnsMut<'a, T> {
    #[inline(always)]
    fn from_slice(data: &'a mut [T], rowstride: usize) -> Self {
        ColumnsMut {
            data: data,
            rowstride: rowstride,
            curr: 0,
        }
    }
}

impl<'a, T: 'a> Iterator for ColumnsMut<'a, T> {
    type Item = ColumnMut<'a, T>;
    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        if self.curr < self.rowstride {
           let data = unsafe { &mut *(&mut self.data[self.curr..] as *mut [T]) };
            self.curr += 1;
            Some(ColumnMut {
                data: data,
                rowstride: self.rowstride,
            })
        } else {
            None
        }
    }
}

struct ColumnMut<'a, T: 'a> {
    data: &'a mut [T],
    rowstride: usize,
}

impl<'a, T: 'a> Index<usize> for ColumnMut<'a, T> {
    type Output = T;
    #[inline(always)]
    fn index(&self, idx: usize) -> &T {
       &self.data[idx * self.rowstride]
    }
}

impl<'a, T: 'a> IndexMut<usize> for ColumnMut<'a, T> {
    #[inline(always)]
    fn index_mut(&mut self, idx: usize) -> &mut T {
       &mut self.data[idx * self.rowstride]
    }
}

thread_local! {
    static PRECOMPUTED_MATRIX: RefCell<Vec<f64>> = RefCell::new(Vec::new());
}

/// Precompute the DCT matrix for a given hash size and memoize it in thread-local
/// storage.
///
///
/// If a precomputed matrix was already stored, even of the same length, it will be overwritten.
///
/// This can produce a significant runtime savings (an order of magnitude on the author's machine)
/// when performing multiple hashing runs with the same hash size, as compared to not performing
/// this step.
///
/// ## Note
/// This only affects the built-in DCT hash (`HashType::DCT`). It also includes
/// the hash size multiplier applied by the DCT hash algorithm, so just pass the same
/// hash size that you would to `ImageHash::hash()`.
///
/// ## Note: Thread-Local
/// Because this uses thread-local storage, this will need to be called on every thread
/// that will be using the DCT hash for the runtime benefit. You can also
/// have precomputed matrices of different sizes for each thread.
pub fn precompute_dct_matrix(size: u32) {
    // The DCT hash uses a hash size larger than the user provided, so we have to
    // precompute a matrix of the right size
    let size = size * ::DCT_HASH_SIZE_MULTIPLIER;
    precomp_exact(size);
}

/// Precompute a DCT matrix of the exact given size.
pub fn precomp_exact(size: u32) {
    PRECOMPUTED_MATRIX.with(|matrix| precompute_matrix(size as usize, &mut matrix.borrow_mut()));
}

#[cfg(feature = "bench")]
pub fn clear_precomputed_matrix() {
    PRECOMPUTED_MATRIX.with(|matrix| matrix.borrow_mut().clear());
}

fn precompute_matrix(size: usize, matrix: &mut Vec<f64>) {
    matrix.resize(size * size, 0.0);

    for i in 0 .. size {
        for j in 0 .. size {
            matrix[i * size + j] = (PI * i as f64 * (2 * j + 1) as f64 / (2 * size) as f64).cos();
        }
    }
}

fn with_precomputed_matrix<F>(size: usize, with_fn: F) -> bool
where F: FnOnce(&[f64]) {
    PRECOMPUTED_MATRIX.with(|matrix| {
        let matrix = matrix.borrow();

        if matrix.len() == size * size {
            with_fn(&matrix);
            true
        } else {
            false
        }
    })
}

pub fn dct_1d<I: Index<usize, Output=f64> + ?Sized, O: IndexMut<usize, Output=f64> + ?Sized>(input: &I, output: &mut O, len: usize) {
    if with_precomputed_matrix(len, |matrix| dct_1d_precomputed(input, output, len, matrix)) {
        return;
    }

    for i in 0 .. len {
        let mut z = 0.0;

        for j in 0 .. len {
            z += input[j] * (
                PI * i as f64 * (2 * j + 1) as f64
                / (2 * len) as f64
            ).cos();
        }

        if i == 0 {
            z *= 1.0 / SQRT_2;
        }

        output[i] = z / 2.0;
    }
}

fn dct_1d_precomputed<I: ?Sized, O: ?Sized>(input: &I, output: &mut O, len: usize, matrix: &[f64])
where I: Index<usize, Output=f64>, O: IndexMut<usize, Output=f64> {
    for i in 0 .. len {
        let mut z = 0.0;

        for j in 0 .. len {
            z += input[j] * matrix[i * len + j];
        }

        if i == 0 {
            z *= 1.0 / SQRT_2;
        }

        output[i] = z / 2.0;
    }
}

/// Perform a 2D DCT on a 1D-packed vector with a given rowstride.
///
/// E.g. a vector of length 9 with a rowstride of 3 will be processed as a 3x3 matrix.
///
/// Returns a vector of the same size packed in the same way.
pub fn dct_2d(packed_2d: &[f64], rowstride: usize) -> Vec<f64> {
    assert_eq!(packed_2d.len() % rowstride, 0);

    let mut scratch = Vec::with_capacity(packed_2d.len() * 2);
    unsafe { scratch.set_len(packed_2d.len() * 2); }

    {
        let (col_pass, row_pass) = scratch.split_at_mut(packed_2d.len());

        for (row_in, row_out) in packed_2d.chunks(rowstride)
                .zip(row_pass.chunks_mut(rowstride)) {
            dct_1d(row_in, row_out, rowstride);
        }

        for (col_in, mut col_out) in Columns::from_slice(row_pass, rowstride)
                .zip(ColumnsMut::from_slice(col_pass, rowstride)) {
            dct_1d(&col_in, &mut col_out, rowstride);
        }
    }

    scratch.truncate(packed_2d.len());
    scratch
}

/*
#[cfg(feature = "simd")]
mod dct_simd {
    use simdty::f64x2;

    use std::f64::consts::{PI, SQRT_2};

    macro_rules! valx2 ( ($val:expr) => ( ::simdty::f64x2($val, $val) ) );

    const PI: f64x2 = valx2!(PI);
    const ONE_DIV_SQRT_2: f64x2 = valx2!(1 / SQRT_2);
    const SQRT_2: f64x2 = valx2!(SQRT_2);

    pub dct_rows(vals: &[Vec<f64>]) -> Vec<Vec<f64>> {
        let mut out = Vec::with_capacity(vals.len());

        for pair in vals.iter().chunks(2) {
            if pair.len() == 2 {
                let vals = pair[0].iter().cloned().zip(pair[1].iter().cloned())
                    .map(f64x2)
                    .collect();

                dct_1dx2(vals);

        }
    }

    fn dct_1dx2(vec: Vec<f64x2>) -> Vec<f64x2> {
        let mut out = Vec::with_capacity(vec.len());

        for u in 0 .. vec.len() {
            let mut z = valx2!(0.0);

            for x in 0 .. vec.len() {
                z += vec[x] * cos_approx(
                    PI * valx2!(
                        u as f64 * (2 * x + 1) as f64
                            / (2 * vec.len()) as f64
                    )
                );
            }

            if u == 0 {
                z *= ONE_DIV_SQRT_2;
            }

            out.insert(u, z / valx2!(2.0));
        }

        out
    }

    fn cos_approx(x2: f64x2) -> f64x2 {
        #[inline(always)]
        fn powi(val: f64x2, pow: i32) -> f64x2 {
            unsafe { llvmint::powi_v2f64(val, pow) }
        }

        let x2 = powi(val, 2);
        let x4 = powi(val, 4);
        let x6 = powi(val, 6);
        let x8 = powi(val, 8);

        valx2!(1.0) - (x2 / valx2!(2.0)) + (x4 / valx2!(24.0))
            - (x6 / valx2!(720.0)) + (x8 / valx2!(40320.0))
    }
}
*/

<|endoftext|>


<commit_before>use std::iter;
use std::collections::vec_deque::VecDeque;
use std::collections::HashSet;
use player::Player;
use cell::Cell;

#[derive(Clone, Debug)]
pub struct DfaState {
  empty: usize,
  red: usize,
  black: usize,
  bad: usize,
  is_final: bool,
  patterns: HashSet<usize>
}

impl DfaState {
  pub fn new(empty: usize, red: usize, black: usize, bad: usize, is_final: bool, patterns: HashSet<usize>) -> DfaState {
    DfaState {
      empty: empty,
      red: red,
      black: black,
      bad: bad,
      is_final: is_final,
      patterns: patterns
    }
  }
}

#[derive(Clone, Debug)]
pub struct Dfa {
  states: Vec<DfaState>
}

impl Dfa {
  pub fn empty() -> Dfa {
    let state = DfaState::new(0, 0, 0, 0, true, HashSet::with_capacity(0));
    Dfa {
      states: vec![state]
    }
  }

  pub fn new(states: Vec<DfaState>) -> Dfa {
    Dfa {
      states: states
    }
  }

  pub fn is_empty(&self) -> bool {
    self.states.len() == 1 && self.states[0].is_final == true && self.states[0].patterns.is_empty()
  }

  pub fn product(&self, other: &Dfa) -> Dfa {
    if self.is_empty() {
      return other.clone();
    }
    if other.is_empty() {
      return self.clone();
    }
    let self_len = self.states.len();
    let other_len = other.states.len();
    let mut new_states = Vec::with_capacity(self_len * other_len);
    for self_state in &self.states {
      let base_empty = self_state.empty * other_len;
      let base_red = self_state.red * other_len;
      let base_black = self_state.black * other_len;
      let base_bad = self_state.bad * other_len;
      for other_state in &other.states {
        let new_state = DfaState {
          empty: base_empty + other_state.empty,
          red: base_red + other_state.red,
          black: base_black + other_state.black,
          bad: base_bad + other_state.bad,
          is_final: self_state.is_final && other_state.is_final,
          patterns: self_state.patterns.union(&other_state.patterns).cloned().collect()
        };
        new_states.push(new_state);
      }
    }
    Dfa {
      states: new_states
    }
  }

  pub fn run<T: Iterator<Item = Cell>>(&self, iter: &mut T) -> &HashSet<usize> {
    if self.is_empty() {
      return &self.states[0].patterns;
    }
    let mut state_idx = 0usize;
    loop {
      let state = &self.states[state_idx as usize];
      if state.is_final { //TODO: parametrize state.patterns.non_empty
        return &state.patterns;
      }
      if let Some(cell) = iter.next() {
        if cell.is_bad() {
          state_idx = state.bad;
        } else if let Some(player) = cell.get_owner() {
          match player {
            Player::Red => state_idx = state.red,
            Player::Black => state_idx = state.black
          }
        } else {
          state_idx = state.empty;
        }
      } else {
        return &state.patterns;
      }
    }
  }

  fn delete_states(&mut self, states_for_delete: Vec<usize>) {
    let mut shifts = Vec::with_capacity(self.states.len());
    for sd in &states_for_delete {
      let shift = shifts.last().map_or(0, |&d| d) + sd;
      shifts.push(shift);
    }
    let deletions = shifts.last().map_or(0, |&d| d);
    if deletions == 0 {
      return;
    }
    let mut new_states = Vec::with_capacity(self.states.len() - deletions);
    for state in self.states.iter().zip(states_for_delete.into_iter()).filter_map(|(state, sd)| if sd == 0 { Some(state) } else { None }) {
      let new_state = DfaState {
        empty: state.empty - shifts[state.empty],
        red: state.red - shifts[state.red],
        black: state.black - shifts[state.black],
        bad: state.bad - shifts[state.bad],
        is_final: state.is_final,
        patterns: state.patterns.clone() //TODO: no clone
      };
      new_states.push(new_state);
    }
    self.states = new_states;
  }

  pub fn delete_non_reachable(&mut self) {
    if self.is_empty() {
      return;
    }
    let mut non_reachable = iter::repeat(1).take(self.states.len()).collect::<Vec<usize>>();
    non_reachable[0] = 0;
    let mut q = VecDeque::with_capacity(self.states.len());
    q.push_back(0);
    while let Some(idx) = q.pop_front() {
      let state = &self.states[idx];
      if non_reachable[state.empty] == 1 {
        non_reachable[state.empty] = 0;
        q.push_back(state.empty);
      }
      if non_reachable[state.red] == 1 {
        non_reachable[state.red] = 0;
        q.push_back(state.red);
      }
      if non_reachable[state.black] == 1 {
        non_reachable[state.black] = 0;
        q.push_back(state.black);
      }
      if non_reachable[state.bad] == 1 {
        non_reachable[state.bad] = 0;
        q.push_back(state.bad);
      }
    }
    self.delete_states(non_reachable);
  }

  fn pyramid_idx_base(i: usize) -> usize {
    i * (i - 1) / 2
  }

  fn pyramid_idx(i: usize, j: usize) -> usize {
    Dfa::pyramid_idx_base(i) + j
  }

  pub fn minimize(&mut self) { //TODO: delete unnecesarry states at the end.
    if self.is_empty() {
      return;
    }
    let len = self.states.len();
    let mut not_equal = iter::repeat(0).take(len * (len - 1) / 2 + len - 1).collect::<Vec<u32>>();
    for (i, pattern_i) in self.states.iter().enumerate().skip(1) {
      let base = Dfa::pyramid_idx_base(i);
      for (j, pattern_j) in self.states[.. i - 1].iter().enumerate() {
        if pattern_i.is_final != pattern_j.is_final || pattern_i.patterns != pattern_j.patterns {
          not_equal[base + j] = 1;
        }
      }
    }
    'outer: loop {
      for (i, pattern_i) in self.states.iter().enumerate().skip(1) {
        let base = Dfa::pyramid_idx_base(i);
        for (j, pattern_j) in self.states[.. i - 1].iter().enumerate() {
          let idx = base + j;
          if not_equal[idx] == 0 {
            if pattern_i.empty != pattern_j.empty && not_equal[Dfa::pyramid_idx(pattern_i.empty, pattern_j.empty)] == 1 ||
               pattern_i.red != pattern_j.red && not_equal[Dfa::pyramid_idx(pattern_i.red, pattern_j.red)] == 1 ||
               pattern_i.black != pattern_j.black && not_equal[Dfa::pyramid_idx(pattern_i.black, pattern_j.black)] == 1 ||
               pattern_i.bad != pattern_j.bad && not_equal[Dfa::pyramid_idx(pattern_i.bad, pattern_j.bad)] == 1 {
              not_equal[idx] = 1;
              continue 'outer;
            }
          }
        }
      }
      break;
    }
    let mut deleted = iter::repeat(0).take(self.states.len()).collect::<Vec<usize>>();
    for i in 1 .. self.states.len() {
      let base = Dfa::pyramid_idx_base(i);
      for j in 0 .. i - 1 {
        let idx = base + j;
        if not_equal[idx] == 0 && deleted[j] == 0 {
          for state in &mut self.states {
            if state.empty == i {
              state.empty = j;
            }
            if state.red == i {
              state.red = j;
            }
            if state.black == i {
              state.black = j;
            }
            if state.bad == i {
              state.bad = j;
            }
          }
          deleted[i] = 1;
        }
      }
    }
    self.delete_states(deleted);
  }
}
<commit_msg>Small DFA optimization.<commit_after>use std::iter;
use std::collections::vec_deque::VecDeque;
use std::collections::HashSet;
use player::Player;
use cell::Cell;

#[derive(Clone, Debug)]
pub struct DfaState {
  empty: usize,
  red: usize,
  black: usize,
  bad: usize,
  is_final: bool,
  patterns: HashSet<usize>
}

impl DfaState {
  pub fn new(empty: usize, red: usize, black: usize, bad: usize, is_final: bool, patterns: HashSet<usize>) -> DfaState {
    DfaState {
      empty: empty,
      red: red,
      black: black,
      bad: bad,
      is_final: is_final,
      patterns: patterns
    }
  }
}

#[derive(Clone, Debug)]
pub struct Dfa {
  states: Vec<DfaState>
}

impl Dfa {
  pub fn empty() -> Dfa {
    let state = DfaState::new(0, 0, 0, 0, true, HashSet::with_capacity(0));
    Dfa {
      states: vec![state]
    }
  }

  pub fn new(states: Vec<DfaState>) -> Dfa {
    Dfa {
      states: states
    }
  }

  pub fn is_empty(&self) -> bool {
    self.states[0].is_final == true && self.states[0].patterns.is_empty()
  }

  pub fn product(&self, other: &Dfa) -> Dfa {
    if self.is_empty() {
      return other.clone();
    }
    if other.is_empty() {
      return self.clone();
    }
    let self_len = self.states.len();
    let other_len = other.states.len();
    let mut new_states = Vec::with_capacity(self_len * other_len);
    for self_state in &self.states {
      let base_empty = self_state.empty * other_len;
      let base_red = self_state.red * other_len;
      let base_black = self_state.black * other_len;
      let base_bad = self_state.bad * other_len;
      for other_state in &other.states {
        let new_state = DfaState {
          empty: base_empty + other_state.empty,
          red: base_red + other_state.red,
          black: base_black + other_state.black,
          bad: base_bad + other_state.bad,
          is_final: self_state.is_final && other_state.is_final,
          patterns: self_state.patterns.union(&other_state.patterns).cloned().collect()
        };
        new_states.push(new_state);
      }
    }
    Dfa {
      states: new_states
    }
  }

  pub fn run<T: Iterator<Item = Cell>>(&self, iter: &mut T) -> &HashSet<usize> {
    if self.is_empty() {
      return &self.states[0].patterns;
    }
    let mut state_idx = 0usize;
    loop {
      let state = &self.states[state_idx as usize];
      if state.is_final { //TODO: parametrize state.patterns.non_empty
        return &state.patterns;
      }
      if let Some(cell) = iter.next() {
        if cell.is_bad() {
          state_idx = state.bad;
        } else if let Some(player) = cell.get_owner() {
          match player {
            Player::Red => state_idx = state.red,
            Player::Black => state_idx = state.black
          }
        } else {
          state_idx = state.empty;
        }
      } else {
        return &state.patterns;
      }
    }
  }

  fn delete_states(&mut self, states_for_delete: Vec<usize>) {
    let mut shifts = Vec::with_capacity(self.states.len());
    for sd in &states_for_delete {
      let shift = shifts.last().map_or(0, |&d| d) + sd;
      shifts.push(shift);
    }
    let deletions = shifts.last().map_or(0, |&d| d);
    if deletions == 0 {
      return;
    }
    let mut new_states = Vec::with_capacity(self.states.len() - deletions);
    for state in self.states.iter().zip(states_for_delete.into_iter()).filter_map(|(state, sd)| if sd == 0 { Some(state) } else { None }) {
      let new_state = DfaState {
        empty: state.empty - shifts[state.empty],
        red: state.red - shifts[state.red],
        black: state.black - shifts[state.black],
        bad: state.bad - shifts[state.bad],
        is_final: state.is_final,
        patterns: state.patterns.clone() //TODO: no clone
      };
      new_states.push(new_state);
    }
    self.states = new_states;
  }

  pub fn delete_non_reachable(&mut self) {
    if self.is_empty() {
      return;
    }
    let mut non_reachable = iter::repeat(1).take(self.states.len()).collect::<Vec<usize>>();
    non_reachable[0] = 0;
    let mut q = VecDeque::with_capacity(self.states.len());
    q.push_back(0);
    while let Some(idx) = q.pop_front() {
      let state = &self.states[idx];
      if non_reachable[state.empty] == 1 {
        non_reachable[state.empty] = 0;
        q.push_back(state.empty);
      }
      if non_reachable[state.red] == 1 {
        non_reachable[state.red] = 0;
        q.push_back(state.red);
      }
      if non_reachable[state.black] == 1 {
        non_reachable[state.black] = 0;
        q.push_back(state.black);
      }
      if non_reachable[state.bad] == 1 {
        non_reachable[state.bad] = 0;
        q.push_back(state.bad);
      }
    }
    self.delete_states(non_reachable);
  }

  fn pyramid_idx_base(i: usize) -> usize {
    i * (i - 1) / 2
  }

  fn pyramid_idx(i: usize, j: usize) -> usize {
    Dfa::pyramid_idx_base(i) + j
  }

  pub fn minimize(&mut self) { //TODO: delete unnecesarry states at the end.
    if self.is_empty() {
      return;
    }
    let len = self.states.len();
    let mut not_equal = iter::repeat(0).take(len * (len - 1) / 2 + len - 1).collect::<Vec<u32>>();
    for (i, pattern_i) in self.states.iter().enumerate().skip(1) {
      let base = Dfa::pyramid_idx_base(i);
      for (j, pattern_j) in self.states[.. i - 1].iter().enumerate() {
        if pattern_i.is_final != pattern_j.is_final || pattern_i.patterns != pattern_j.patterns {
          not_equal[base + j] = 1;
        }
      }
    }
    'outer: loop {
      for (i, pattern_i) in self.states.iter().enumerate().skip(1) {
        let base = Dfa::pyramid_idx_base(i);
        for (j, pattern_j) in self.states[.. i - 1].iter().enumerate() {
          let idx = base + j;
          if not_equal[idx] == 0 {
            if pattern_i.empty != pattern_j.empty && not_equal[Dfa::pyramid_idx(pattern_i.empty, pattern_j.empty)] == 1 ||
               pattern_i.red != pattern_j.red && not_equal[Dfa::pyramid_idx(pattern_i.red, pattern_j.red)] == 1 ||
               pattern_i.black != pattern_j.black && not_equal[Dfa::pyramid_idx(pattern_i.black, pattern_j.black)] == 1 ||
               pattern_i.bad != pattern_j.bad && not_equal[Dfa::pyramid_idx(pattern_i.bad, pattern_j.bad)] == 1 {
              not_equal[idx] = 1;
              continue 'outer;
            }
          }
        }
      }
      break;
    }
    let mut deleted = iter::repeat(0).take(self.states.len()).collect::<Vec<usize>>();
    for i in 1 .. self.states.len() {
      let base = Dfa::pyramid_idx_base(i);
      for j in 0 .. i - 1 {
        let idx = base + j;
        if not_equal[idx] == 0 && deleted[j] == 0 {
          for state in &mut self.states {
            if state.empty == i {
              state.empty = j;
            }
            if state.red == i {
              state.red = j;
            }
            if state.black == i {
              state.black = j;
            }
            if state.bad == i {
              state.bad = j;
            }
          }
          deleted[i] = 1;
        }
      }
    }
    self.delete_states(deleted);
  }
}
<|endoftext|>


<commit_before>#[macro_export]

/// Creates callbacks for Ruby methods
///
/// # Examples
///
/// ```no_run
/// #[macro_use]
/// extern crate ruru;
///
/// use ruru::{AnyObject, Boolean, Class, Fixnum, RString, VM};
/// use ruru::types::Argc;
/// use ruru::traits::Object;
///
/// // Creates `string_is_blank` and `string_length_equals` functions
/// methods!(
///     RString, // type of `self` object
///     itself, // name of `self` object which will be used in methods
///
///     string_is_blank() -> Boolean {
///         Boolean::new(itself.to_string().chars().all(|c| c.is_whitespace()))
///     }
///
///     string_length_equals(expected_length: Fixnum) -> Boolean {
///         let real_length = itself.to_string().len() as i64;
///
///         Boolean::new(expected_length.to_i64() == real_length)
///     }
/// );
///
/// fn main() {
///     # VM::init();
///     Class::from_existing("String").define(|itself| {
///         itself.def("blank?", string_is_blank);
///         itself.def("length_equals?", string_length_equals);
///     });
/// }
/// ```
///
/// Ruby:
///
/// ```ruby
/// class String
///   def blank?
///     # ...
///   end
///
///   def length_equals?(expected_length)
///     # ...
///   end
/// end
/// ```
macro_rules! methods {
    (
        $itself_class: ty,
        $itself_name: ident,
        $(
            $method_name: ident
            ($($arg_name: ident: $arg_type: ty),*) -> $return_type: ident $body: block
        )*
    ) => {
        $(
            #[no_mangle]
            pub extern fn $method_name(argc: Argc,
                                       argv: *const AnyObject,
                                       mut $itself_name: $itself_class) -> $return_type {
                let arguments = VM::parse_arguments(argc, argv);
                let mut i = 0;

                $(
                    let $arg_name = arguments[i].to::<$arg_type>();
                    i += 1;
                )*

                $body
            }
        )*
    }
}
<commit_msg>Create class! macro to create Rust structs<commit_after>/// Creates Rust structure for new Ruby class
///
/// # Examples
///
/// ```no_run
/// #[macro_use]
/// extern crate ruru;
///
/// use ruru::{AnyObject, Class, RString, VM};
/// use ruru::types::{Argc, Value};
/// use ruru::traits::Object;
///
/// class!(Greeter);
///
/// methods!(
///     Greeter,
///     itself,
///
///     anonymous_greeting() -> RString {
///         RString::new("Hello stranger!")
///     }
///
///     friendly_greeting(name: RString) -> RString {
///         let greeting = format!("Hello dear {}!", name.to_string());
///
///         RString::new(&greeting)
///     }
/// );
///
/// fn main() {
///     # VM::init();
///     Class::new("Greeter").define(|itself| {
///         itself.def("anonymous_greeting", anonymous_greeting);
///         itself.def("friendly_greeting", friendly_greeting);
///     });
/// }
/// ```
///
/// Ruby:
///
/// ```ruby
/// class Greeter
///   def anonymous_greeting
///     'Hello stranger!'
///   end
///
///   def friendly_greeting(name)
///     "Hello dear #{name}"
///   end
/// end
/// ```
#[macro_export]
macro_rules! class {
    ($class: ident) => {
        pub struct $class {
            value: Value,
        }

        impl From<Value> for $class {
            fn from(value: Value) -> Self {
                $class { value: value }
            }
        }

        impl Object for $class {
            fn value(&self) -> Value {
                self.value
            }
        }
    }
}

/// Creates callbacks for Ruby methods
///
/// # Examples
///
/// ```no_run
/// #[macro_use]
/// extern crate ruru;
///
/// use ruru::{AnyObject, Boolean, Class, Fixnum, RString, VM};
/// use ruru::types::Argc;
/// use ruru::traits::Object;
///
/// // Creates `string_is_blank` and `string_length_equals` functions
/// methods!(
///     RString, // type of `self` object
///     itself, // name of `self` object which will be used in methods
///
///     string_is_blank() -> Boolean {
///         Boolean::new(itself.to_string().chars().all(|c| c.is_whitespace()))
///     }
///
///     string_length_equals(expected_length: Fixnum) -> Boolean {
///         let real_length = itself.to_string().len() as i64;
///
///         Boolean::new(expected_length.to_i64() == real_length)
///     }
/// );
///
/// fn main() {
///     # VM::init();
///     Class::from_existing("String").define(|itself| {
///         itself.def("blank?", string_is_blank);
///         itself.def("length_equals?", string_length_equals);
///     });
/// }
/// ```
///
/// Ruby:
///
/// ```ruby
/// class String
///   def blank?
///     # ...
///   end
///
///   def length_equals?(expected_length)
///     # ...
///   end
/// end
/// ```
#[macro_export]
macro_rules! methods {
    (
        $itself_class: ty,
        $itself_name: ident,
        $(
            $method_name: ident
            ($($arg_name: ident: $arg_type: ty),*) -> $return_type: ident $body: block
        )*
    ) => {
        $(
            #[no_mangle]
            pub extern fn $method_name(argc: Argc,
                                       argv: *const AnyObject,
                                       mut $itself_name: $itself_class) -> $return_type {
                let arguments = VM::parse_arguments(argc, argv);
                let mut i = 0;

                $(
                    let $arg_name = arguments[i].to::<$arg_type>();
                    i += 1;
                )*

                $body
            }
        )*
    }
}
<|endoftext|>


<commit_before>use libc::{c_int, c_char, c_void};

#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct IPaddress { pub host: u32, pub port: u16 }
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct _TCPsocket;
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct _SDLNet_SocketSet;
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct _SDLNet_GenericSocket;
unsafe impl Sync for *const _TCPsocket {}
unsafe impl Sync for *const _SDLNet_SocketSet {}

// Linking setup (using https://github.com/xsleonard/rust-sdl2_image/ as an example)
#[cfg(target_os="macos")]
mod mac {
    //#[cfg(mac_framework)]
    //#[link(kind="framework", name="SDL2_net")]
    //extern {}

    //#[cfg(not(mac_framework))]
    #[link(name="SDL2_net")]
    extern {}
}
#[cfg(any(target_os="linux", target_os="freebsd", target_os="windows"))]
mod others {
    #[link(name="SDL2_net")]
    extern {}
}

extern "C" {
    //General
    pub fn SDLNet_Init() -> c_int;
    pub fn SDLNet_Quit() -> ();
    pub fn SDLNet_GetError() -> *const c_char;

    // Name Resolution
    pub fn SDLNet_ResolveHost(address: *mut IPaddress, host: *const c_char, port: u16) -> c_int;
    pub fn SDLNet_ResolveIP(address: *mut IPaddress) -> *const c_char;

    // TCP Sockets
    pub fn SDLNet_TCP_Open(ip: *mut IPaddress) -> *mut _TCPsocket;
    pub fn SDLNet_TCP_Close(sock: *const _TCPsocket) -> ();
    pub fn SDLNet_TCP_Accept(server: *const _TCPsocket) -> *mut _TCPsocket;
    pub fn SDLNet_TCP_GetPeerAddress(sock: *const _TCPsocket) -> *mut IPaddress;
    pub fn SDLNet_TCP_Send(sock: *const _TCPsocket, data : *const c_void, len: c_int) -> c_int;
    pub fn SDLNet_TCP_Recv(sock: *const _TCPsocket, data: *mut c_void, maxlen: c_int) -> c_int;

    // UDP Sockets

    // UDP Packets

    // Socket Sets
    pub fn SDLNet_AllocSocketSet(maxsockets: c_int) -> *const _SDLNet_SocketSet;
    pub fn SDLNet_FreeSocketSet(set: *const _SDLNet_SocketSet) -> ();
    pub fn SDLNet_AddSocket(set: *const _SDLNet_SocketSet, sock: *const _TCPsocket) -> c_int;
    pub fn SDLNet_DelSocket(set: *const _SDLNet_SocketSet, sock: *const _TCPsocket) -> c_int;
    pub fn SDLNet_CheckSockets(set: *const _SDLNet_SocketSet, timeout: u32) -> c_int;
    pub fn SDLNet_SocketReady(sock: *mut c_void) -> c_int; // documentation unclear as to what it takes in - assmuing tcp for now
}

<commit_msg>Added some method declarations into ffi.rs, unwraped.<commit_after>use libc::{c_int, c_char, c_void};

#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct IPaddress { pub host: u32, pub port: u16 }
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct _TCPsocket;
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct _UDPsocket;
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct _UDPpacket;
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct _SDLNet_SocketSet;
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct _SDLNet_GenericSocket;
unsafe impl Sync for *const _TCPsocket {}
unsafe impl Sync for *const _UDPsocket {}
unsafe impl Sync for *const _SDLNet_SocketSet {}

// Linking setup (using https://github.com/xsleonard/rust-sdl2_image/ as an example)
#[cfg(target_os="macos")]
mod mac {
    //#[cfg(mac_framework)]
    //#[link(kind="framework", name="SDL2_net")]
    //extern {}

    //#[cfg(not(mac_framework))]
    #[link(name="SDL2_net")]
    extern {}
}
#[cfg(any(target_os="linux", target_os="freebsd", target_os="windows"))]
mod others {
    #[link(name="SDL2_net")]
    extern {}
}

extern "C" {
    //General
    pub fn SDLNet_Init() -> c_int;
    pub fn SDLNet_Quit() -> ();
    pub fn SDLNet_GetError() -> *const c_char;

    // Name Resolution
    pub fn SDLNet_ResolveHost(address: *mut IPaddress, host: *const c_char, port: u16) -> c_int;
    pub fn SDLNet_ResolveIP(address: *mut IPaddress) -> *const c_char;

    // TCP Sockets
    pub fn SDLNet_TCP_Open(ip: *mut IPaddress) -> *mut _TCPsocket;
    pub fn SDLNet_TCP_Close(sock: *const _TCPsocket) -> ();
    pub fn SDLNet_TCP_Accept(server: *const _TCPsocket) -> *mut _TCPsocket;
    pub fn SDLNet_TCP_GetPeerAddress(sock: *const _TCPsocket) -> *mut IPaddress;
    pub fn SDLNet_TCP_Send(sock: *const _TCPsocket, data : *const c_void, len: c_int) -> c_int;
    pub fn SDLNet_TCP_Recv(sock: *const _TCPsocket, data: *mut c_void, maxlen: c_int) -> c_int;

    // UDP Sockets
    pub fn SDLNet_UDP_Open(port: u16) -> *mut _UDPsocket;
    pub fn SDLNet_UDP_Close(sock: *const _UDPsocket) -> ();
    pub fn SDLNet_UDP_Bind(sock: *const _UDPsocket, channel: i32, ip: *mut IPaddress) -> c_int;
    pub fn SDLNet_UDP_Unbind(sock: *const _UDPsocket, channel: i32) -> ();
    pub fn SDLNet_UDP_GetPeerAddress(sock: *const _UDPsocket, channel: i32) -> *mut IPaddress;
    pub fn SDLNet_UDP_Send(sock: *const _UDPsocket, channel: i32, packet: *mut _UDPpacket) ->
        c_int;
    pub fn SDLNet_UDP_Recv(sock: *const _UDPsocket, packet: *mut _UDPpacket) -> c_int;
//  pub fn SDLNet_UDP_SendV ...
//  pub fn SDLNet_UDP_RecvV ...

    // UDP Packets
    pub fn SDLNet_AllocPacket(size: i32) -> *mut _UDPpacket;
    pub fn SDLNet_ResizePacket(packet: *mut _UDPpacket, size: i32) -> c_int;
    pub fn SDLNet_FreePacket(packet: *mut _UDPpacket) -> ();
//  pub fn SDLNet_AllocPacketV ...
//  pub fn SDLNet_FreePacketV ...

    // Socket Sets
    pub fn SDLNet_AllocSocketSet(maxsockets: c_int) -> *const _SDLNet_SocketSet;
    pub fn SDLNet_FreeSocketSet(set: *const _SDLNet_SocketSet) -> ();
    pub fn SDLNet_AddSocket(set: *const _SDLNet_SocketSet, sock: *const _TCPsocket) -> c_int;
    pub fn SDLNet_DelSocket(set: *const _SDLNet_SocketSet, sock: *const _TCPsocket) -> c_int;
    pub fn SDLNet_CheckSockets(set: *const _SDLNet_SocketSet, timeout: u32) -> c_int;
    pub fn SDLNet_SocketReady(sock: *mut _SDLNet_GenericSocket) -> c_int; // documentation unclear as to what it takes in - assmuing tcp for now
}

<|endoftext|>


<commit_before>use std::vec::Vec;
use std::path::Path;
use std::str;

use super::git2;
use super::RepositoryConfiguration;

pub struct Repository<'repo> {
    pub repository: git2::Repository,
    details: &'repo RepositoryConfiguration,
}

pub struct Remote<'repo> {
    pub remote: git2::Remote<'repo>,
    repository: &'repo Repository<'repo>,
}

#[derive(Clone, Debug)]
pub struct RemoteHead {
    pub is_local: bool,
    pub oid: git2::Oid,
    pub loid: git2::Oid,
    pub name: String,
    pub symref_target: Option<String>,
}

impl RemoteHead {
    pub fn flatten(&self) -> &str {
        match self.symref_target {
            Some(ref s) => s,
            _ => &self.name,
        }
    }

    pub fn flatten_clone(&self) -> String {
        self.flatten().to_string()
    }
}

impl<'repo> Repository<'repo> {
    pub fn new(repository: git2::Repository, configuration: &'repo RepositoryConfiguration) -> Repository<'repo> {
        Repository {
            repository: repository,
            details: configuration,
        }
    }

    pub fn clone_or_open(repo_details: &'repo RepositoryConfiguration) -> Result<Repository<'repo>, git2::Error> {
        Repository::open(repo_details).or_else(|err| if err.code() == git2::ErrorCode::NotFound {
            info!("Repository not found at {} -- cloning",
                  repo_details.checkout_path);
            Repository::clone(repo_details)
        } else {
            Err(err)
        })
    }

    pub fn open(repo_details: &'repo RepositoryConfiguration) -> Result<Repository<'repo>, git2::Error> {
        info!("Opening repository at {}", &repo_details.checkout_path);
        git2::Repository::open(&repo_details.checkout_path).and_then(|repo| Ok(Repository::new(repo, repo_details)))
    }

    pub fn clone(repo_details: &'repo RepositoryConfiguration) -> Result<Repository<'repo>, git2::Error> {
        let remote_callbacks = Repository::remote_callbacks(repo_details);

        let mut fetch_optoons = git2::FetchOptions::new();
        fetch_optoons.remote_callbacks(remote_callbacks);

        let mut repo_builder = git2::build::RepoBuilder::new();
        repo_builder.fetch_options(fetch_optoons);

        info!("Cloning repository from {} into {}",
              repo_details.uri,
              repo_details.checkout_path);
        repo_builder.clone(&repo_details.uri, &Path::new(&repo_details.checkout_path))
            .and_then(|repo| Ok(Repository::new(repo, repo_details)))
    }

    fn remote_callbacks(repo_details: &'repo RepositoryConfiguration) -> git2::RemoteCallbacks<'repo> {
        debug!("Making remote authentication callbacks");
        let mut remote_callbacks = git2::RemoteCallbacks::new();
        let repo_details = repo_details.clone();
        remote_callbacks.credentials(move |_uri, username, cred_type| {
                let username = username.or_else(|| match repo_details.username {
                    Some(ref username) => Some(username),
                    None => None,
                });
                if cred_type.intersects(git2::USERNAME) && username.is_some() {
                    git2::Cred::username(username.as_ref().unwrap())
                } else if cred_type.intersects(git2::USER_PASS_PLAINTEXT) && username.is_some() &&
                          repo_details.password.is_some() {
                    git2::Cred::userpass_plaintext(username.as_ref().unwrap(),
                                                   repo_details.password.as_ref().unwrap())
                } else if cred_type.intersects(git2::SSH_KEY) && username.is_some() {
                    if repo_details.key.is_some() {
                        git2::Cred::ssh_key(username.unwrap(),
                                            None,
                                            Path::new(repo_details.key.as_ref().unwrap()),
                                            repo_details.key_passphrase.as_ref().map(|x| &**x))
                    } else {
                        git2::Cred::ssh_key_from_agent(username.unwrap())
                    }

                } else {
                    let config = git2::Config::open_default()?;
                    git2::Cred::credential_helper(&config, &repo_details.uri, username)
                }
            })
            .transfer_progress(|progress| {
                // TODO: Maybe throttle this, or update UI
                if progress.received_objects() == progress.total_objects() {
                    debug!("Resolving deltas {}/{}\r",
                           progress.indexed_deltas(),
                           progress.total_deltas());
                } else if progress.total_objects() > 0 {
                    debug!("Received {}/{} objects ({}) in {} bytes\r",
                           progress.received_objects(),
                           progress.total_objects(),
                           progress.indexed_objects(),
                           progress.received_bytes());
                }
                true
            })
            .sideband_progress(|data| {
                debug!("remote: {}", str::from_utf8(data).unwrap_or(""));
                true
            })
            .update_tips(|refname, a, b| {
                if a.is_zero() {
                    debug!("[new]     {:20} {}", b, refname);
                } else {
                    debug!("[updated] {:10}..{:10} {}", a, b, refname);
                }
                true
            });
        remote_callbacks
    }

    pub fn remote(&self, remote: Option<&str>) -> Result<Remote, git2::Error> {
        Ok(Remote {
            remote: self.repository.find_remote(&Repository::remote_name_or_default(remote))?,
            repository: self,
        })
    }

    pub fn remote_name_or_default(remote: Option<&str>) -> String {
        remote.unwrap_or("origin").to_string()
    }

    pub fn signature(&self) -> Result<git2::Signature, git2::Error> {
        if self.details.signature_name.is_some() && self.details.signature_email.is_some() {
            return git2::Signature::now(self.details.signature_name.as_ref().unwrap(),
                                        self.details.signature_email.as_ref().unwrap());
        }

        match self.repository.signature() {
            Ok(signature) => Ok(signature),
            Err(_) => git2::Signature::now("fusionner", "fusionner@github.com"),
        }
    }
}

impl<'repo> Remote<'repo> {
    pub fn connect<'connection>(&'connection mut self)
                                -> Result<git2::RemoteConnection<'repo, 'connection, 'connection>, git2::Error> {
        let callbacks = Repository::remote_callbacks(self.repository.details);
        info!("Connecting to remote");
        self.remote.connect(git2::Direction::Fetch, Some(callbacks), None)
    }

    pub fn disconnect(&mut self) {
        self.remote.disconnect();
    }

    pub fn name(&self) -> Option<&str> {
        self.remote.name()
    }

    pub fn refspecs(&self) -> git2::Refspecs {
        self.remote.refspecs()
    }

    pub fn remote_ls(&mut self) -> Result<Vec<RemoteHead>, git2::Error> {
        let connection = self.connect()?;
        info!("Retrieving remote references `git ls-remote`");
        let heads = connection.list()?;
        Ok(heads.iter()
            .map(|head| {
                RemoteHead {
                    is_local: head.is_local(),
                    oid: head.oid(),
                    loid: head.loid(),
                    name: head.name().to_string(),
                    symref_target: head.symref_target().map(|s| s.to_string()),
                }
            })
            .collect())
    }

    // Get the remote reference of renote HEAD (i.e. default branch)
    pub fn head(&mut self) -> Result<Option<String>, git2::Error> {
        let connection = self.connect()?;
        info!("Retrieving remote references `git ls-remote`");
        let heads = connection.list()?;
        Ok(Remote::resolve_head(heads))
    }

    // Resolve the remote HEAD (i.e. default branch) from a list of heads
    // and return the remote reference
    pub fn resolve_head(heads: &[git2::RemoteHead]) -> Option<String> {
        heads.iter()
            .find(|head| head.name() == "HEAD" && head.symref_target().is_some())
            .and_then(|head| Some(head.symref_target().unwrap().to_string()))
    }

    pub fn fetch(&mut self, refspecs: &[&str]) -> Result<(), git2::Error> {
        let mut fetch_options = git2::FetchOptions::new();
        let callbacks = Repository::remote_callbacks(self.repository.details);
        fetch_options.remote_callbacks(callbacks)
            .prune(git2::FetchPrune::On);

        debug!("Fetching {:?}", refspecs);
        self.remote.fetch(refspecs, Some(&mut fetch_options), None)?;
        self.remote.disconnect();
        Ok(())
    }

    pub fn push(&mut self, refspecs: &[&str]) -> Result<(), git2::Error> {
        let mut push_options = git2::PushOptions::new();
        let callbacks = Repository::remote_callbacks(self.repository.details);
        push_options.remote_callbacks(callbacks);

        self.remote.push(refspecs, Some(&mut push_options))
    }

    /// For a given local reference, generate a refspec for the remote with the same path on remote
    /// i.e. refs/pulls/*  --> refs/pulls/*:refs/remote/origin/pulls/*
    pub fn generate_refspec(&self, src: &str, force: bool) -> Result<String, String> {
        let parts: Vec<&str> = src.split('/').collect();
        if parts[0] != "refs" {
            return Err("Invalid reference -- does not begin with refs/".to_string());
        }
        let prepend = vec!["refs", "remotes", self.name().ok_or("Un-named remote")?];
        let dest: Vec<&&str> = prepend.iter().chain(parts.iter().skip(1)).collect();
        let dest = dest.iter().map(|s| **s).collect::<Vec<&str>>().join("/");

        let force_flag = if force { "+" } else { "" };

        Ok(format!("{}{}:{}", force_flag, src, dest))
    }

    pub fn add_refspec(&self, refspec: &str, direction: git2::Direction) -> Result<(), git2::Error> {
        let remote_name = self.name().ok_or(git2::Error::from_str("Un-named remote used"))?;

        info!("Checking and adding refspec {}", refspec);
        if let None = Remote::find_matching_refspec(self.refspecs(), direction, refspec) {
            match direction {
                git2::Direction::Fetch => {
                    info!("No existing fetch refpec found: adding {}", refspec);
                    self.repository.repository.remote_add_fetch(remote_name, &refspec)?;
                }
                git2::Direction::Push => {
                    info!("No existing push refpec found: adding {}", refspec);
                    self.repository.repository.remote_add_push(remote_name, &refspec)?;
                }
            }
        }

        Ok(())
    }

    pub fn add_refspecs(&self, refspecs: &[&str], direction: git2::Direction) -> Result<(), git2::Error> {
        for refspec in refspecs {
            self.add_refspec(refspec, direction)?
        }
        Ok(())
    }

    fn find_matching_refspec<'a>(mut refspecs: git2::Refspecs<'a>,
                                 direction: git2::Direction,
                                 refspec: &str)
                                 -> Option<git2::Refspec<'a>> {
        refspecs.find(|r| {
            let rs = r.str();
            Remote::direction_eq(&r.direction(), &direction) && rs.is_some() && rs.unwrap() == refspec
        })
    }

    pub fn direction_eq(left: &git2::Direction, right: &git2::Direction) -> bool {
        use git2::Direction::*;

        match (left, right) {
            (&Fetch, &Fetch) => true,
            (&Push, &Push) => true,
            _ => false,
        }
    }
}
<commit_msg>Git smoke tests<commit_after>use std::vec::Vec;
use std::path::Path;
use std::str;

use super::git2;
use super::RepositoryConfiguration;

pub struct Repository<'repo> {
    pub repository: git2::Repository,
    details: &'repo RepositoryConfiguration,
}

pub struct Remote<'repo> {
    pub remote: git2::Remote<'repo>,
    repository: &'repo Repository<'repo>,
}

#[derive(Clone, Debug)]
pub struct RemoteHead {
    pub is_local: bool,
    pub oid: git2::Oid,
    pub loid: git2::Oid,
    pub name: String,
    pub symref_target: Option<String>,
}

impl RemoteHead {
    pub fn flatten(&self) -> &str {
        match self.symref_target {
            Some(ref s) => s,
            _ => &self.name,
        }
    }

    pub fn flatten_clone(&self) -> String {
        self.flatten().to_string()
    }
}

impl<'repo> Repository<'repo> {
    pub fn new(repository: git2::Repository, configuration: &'repo RepositoryConfiguration) -> Repository<'repo> {
        Repository {
            repository: repository,
            details: configuration,
        }
    }

    pub fn clone_or_open(repo_details: &'repo RepositoryConfiguration) -> Result<Repository<'repo>, git2::Error> {
        Repository::open(repo_details).or_else(|err| if err.code() == git2::ErrorCode::NotFound {
            info!("Repository not found at {} -- cloning",
                  repo_details.checkout_path);
            Repository::clone(repo_details)
        } else {
            Err(err)
        })
    }

    pub fn open(repo_details: &'repo RepositoryConfiguration) -> Result<Repository<'repo>, git2::Error> {
        info!("Opening repository at {}", &repo_details.checkout_path);
        git2::Repository::open(&repo_details.checkout_path).and_then(|repo| Ok(Repository::new(repo, repo_details)))
    }

    pub fn clone(repo_details: &'repo RepositoryConfiguration) -> Result<Repository<'repo>, git2::Error> {
        let remote_callbacks = Repository::remote_callbacks(repo_details);

        let mut fetch_optoons = git2::FetchOptions::new();
        fetch_optoons.remote_callbacks(remote_callbacks);

        let mut repo_builder = git2::build::RepoBuilder::new();
        repo_builder.fetch_options(fetch_optoons);

        info!("Cloning repository from {} into {}",
              repo_details.uri,
              repo_details.checkout_path);
        repo_builder.clone(&repo_details.uri, &Path::new(&repo_details.checkout_path))
            .and_then(|repo| Ok(Repository::new(repo, repo_details)))
    }

    fn remote_callbacks(repo_details: &'repo RepositoryConfiguration) -> git2::RemoteCallbacks<'repo> {
        debug!("Making remote authentication callbacks");
        let mut remote_callbacks = git2::RemoteCallbacks::new();
        let repo_details = repo_details.clone();
        remote_callbacks.credentials(move |_uri, username, cred_type| {
                let username = username.or_else(|| match repo_details.username {
                    Some(ref username) => Some(username),
                    None => None,
                });
                if cred_type.intersects(git2::USERNAME) && username.is_some() {
                    git2::Cred::username(username.as_ref().unwrap())
                } else if cred_type.intersects(git2::USER_PASS_PLAINTEXT) && username.is_some() &&
                          repo_details.password.is_some() {
                    git2::Cred::userpass_plaintext(username.as_ref().unwrap(),
                                                   repo_details.password.as_ref().unwrap())
                } else if cred_type.intersects(git2::SSH_KEY) && username.is_some() {
                    if repo_details.key.is_some() {
                        git2::Cred::ssh_key(username.unwrap(),
                                            None,
                                            Path::new(repo_details.key.as_ref().unwrap()),
                                            repo_details.key_passphrase.as_ref().map(|x| &**x))
                    } else {
                        git2::Cred::ssh_key_from_agent(username.unwrap())
                    }

                } else {
                    let config = git2::Config::open_default()?;
                    git2::Cred::credential_helper(&config, &repo_details.uri, username)
                }
            })
            .transfer_progress(|progress| {
                // TODO: Maybe throttle this, or update UI
                if progress.received_objects() == progress.total_objects() {
                    debug!("Resolving deltas {}/{}\r",
                           progress.indexed_deltas(),
                           progress.total_deltas());
                } else if progress.total_objects() > 0 {
                    debug!("Received {}/{} objects ({}) in {} bytes\r",
                           progress.received_objects(),
                           progress.total_objects(),
                           progress.indexed_objects(),
                           progress.received_bytes());
                }
                true
            })
            .sideband_progress(|data| {
                debug!("remote: {}", str::from_utf8(data).unwrap_or(""));
                true
            })
            .update_tips(|refname, a, b| {
                if a.is_zero() {
                    debug!("[new]     {:20} {}", b, refname);
                } else {
                    debug!("[updated] {:10}..{:10} {}", a, b, refname);
                }
                true
            });
        remote_callbacks
    }

    pub fn remote(&self, remote: Option<&str>) -> Result<Remote, git2::Error> {
        Ok(Remote {
            remote: self.repository.find_remote(&Repository::remote_name_or_default(remote))?,
            repository: self,
        })
    }

    pub fn remote_name_or_default(remote: Option<&str>) -> String {
        remote.unwrap_or("origin").to_string()
    }

    pub fn signature(&self) -> Result<git2::Signature, git2::Error> {
        if self.details.signature_name.is_some() && self.details.signature_email.is_some() {
            return git2::Signature::now(self.details.signature_name.as_ref().unwrap(),
                                        self.details.signature_email.as_ref().unwrap());
        }

        match self.repository.signature() {
            Ok(signature) => Ok(signature),
            Err(_) => git2::Signature::now("fusionner", "fusionner@github.com"),
        }
    }
}

impl<'repo> Remote<'repo> {
    pub fn connect<'connection>(&'connection mut self)
                                -> Result<git2::RemoteConnection<'repo, 'connection, 'connection>, git2::Error> {
        let callbacks = Repository::remote_callbacks(self.repository.details);
        info!("Connecting to remote");
        self.remote.connect(git2::Direction::Fetch, Some(callbacks), None)
    }

    pub fn disconnect(&mut self) {
        self.remote.disconnect();
    }

    pub fn name(&self) -> Option<&str> {
        self.remote.name()
    }

    pub fn refspecs(&self) -> git2::Refspecs {
        self.remote.refspecs()
    }

    pub fn remote_ls(&mut self) -> Result<Vec<RemoteHead>, git2::Error> {
        let connection = self.connect()?;
        info!("Retrieving remote references `git ls-remote`");
        let heads = connection.list()?;
        Ok(heads.iter()
            .map(|head| {
                RemoteHead {
                    is_local: head.is_local(),
                    oid: head.oid(),
                    loid: head.loid(),
                    name: head.name().to_string(),
                    symref_target: head.symref_target().map(|s| s.to_string()),
                }
            })
            .collect())
    }

    // Get the remote reference of renote HEAD (i.e. default branch)
    pub fn head(&mut self) -> Result<Option<String>, git2::Error> {
        let connection = self.connect()?;
        info!("Retrieving remote references `git ls-remote`");
        let heads = connection.list()?;
        Ok(Remote::resolve_head(heads))
    }

    // Resolve the remote HEAD (i.e. default branch) from a list of heads
    // and return the remote reference
    pub fn resolve_head(heads: &[git2::RemoteHead]) -> Option<String> {
        heads.iter()
            .find(|head| head.name() == "HEAD" && head.symref_target().is_some())
            .and_then(|head| Some(head.symref_target().unwrap().to_string()))
    }

    pub fn fetch(&mut self, refspecs: &[&str]) -> Result<(), git2::Error> {
        let mut fetch_options = git2::FetchOptions::new();
        let callbacks = Repository::remote_callbacks(self.repository.details);
        fetch_options.remote_callbacks(callbacks)
            .prune(git2::FetchPrune::On);

        debug!("Fetching {:?}", refspecs);
        self.remote.fetch(refspecs, Some(&mut fetch_options), None)?;
        self.remote.disconnect();
        Ok(())
    }

    pub fn push(&mut self, refspecs: &[&str]) -> Result<(), git2::Error> {
        let mut push_options = git2::PushOptions::new();
        let callbacks = Repository::remote_callbacks(self.repository.details);
        push_options.remote_callbacks(callbacks);

        self.remote.push(refspecs, Some(&mut push_options))
    }

    /// For a given local reference, generate a refspec for the remote with the same path on remote
    /// i.e. refs/pulls/*  --> refs/pulls/*:refs/remote/origin/pulls/*
    pub fn generate_refspec(&self, src: &str, force: bool) -> Result<String, String> {
        let parts: Vec<&str> = src.split('/').collect();
        if parts[0] != "refs" {
            return Err("Invalid reference -- does not begin with refs/".to_string());
        }
        let prepend = vec!["refs", "remotes", self.name().ok_or("Un-named remote")?];
        let dest: Vec<&&str> = prepend.iter().chain(parts.iter().skip(1)).collect();
        let dest = dest.iter().map(|s| **s).collect::<Vec<&str>>().join("/");

        let force_flag = if force { "+" } else { "" };

        Ok(format!("{}{}:{}", force_flag, src, dest))
    }

    pub fn add_refspec(&self, refspec: &str, direction: git2::Direction) -> Result<(), git2::Error> {
        let remote_name = self.name().ok_or(git2::Error::from_str("Un-named remote used"))?;

        info!("Checking and adding refspec {}", refspec);
        if let None = Remote::find_matching_refspec(self.refspecs(), direction, refspec) {
            match direction {
                git2::Direction::Fetch => {
                    info!("No existing fetch refpec found: adding {}", refspec);
                    self.repository.repository.remote_add_fetch(remote_name, &refspec)?;
                }
                git2::Direction::Push => {
                    info!("No existing push refpec found: adding {}", refspec);
                    self.repository.repository.remote_add_push(remote_name, &refspec)?;
                }
            }
        }

        Ok(())
    }

    pub fn add_refspecs(&self, refspecs: &[&str], direction: git2::Direction) -> Result<(), git2::Error> {
        for refspec in refspecs {
            self.add_refspec(refspec, direction)?
        }
        Ok(())
    }

    fn find_matching_refspec<'a>(mut refspecs: git2::Refspecs<'a>,
                                 direction: git2::Direction,
                                 refspec: &str)
                                 -> Option<git2::Refspec<'a>> {
        refspecs.find(|r| {
            let rs = r.str();
            Remote::direction_eq(&r.direction(), &direction) && rs.is_some() && rs.unwrap() == refspec
        })
    }

    pub fn direction_eq(left: &git2::Direction, right: &git2::Direction) -> bool {
        use git2::Direction::*;

        match (left, right) {
            (&Fetch, &Fetch) => true,
            (&Push, &Push) => true,
            _ => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use git::Repository;
    use tempdir::TempDir;

    #[test]
    fn smoke_test_opem() {
        let (td, _raw) = ::test::raw_repo_init();
        let config = ::test::config_init(&td);

        not_err!(Repository::clone_or_open(&config));
    }

    #[test]
    fn smoke_test_clone() {
        let (td, _raw) = ::test::raw_repo_init();
        let mut config = ::test::config_init(&td);

        let td_new = TempDir::new("test").unwrap();
        config.checkout_path = not_none!(td_new.path().to_str()).to_string();

        not_err!(Repository::clone_or_open(&config));
    }
}
<|endoftext|>


<commit_before>use gtk;
use gtk::prelude::*;
use std::cell::RefCell;
use portaudio as pa;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::RwLock;
use std::io;
use std::io::Write;
use std::thread;
use std::sync::mpsc::*;

const FPS: u32 = 30;

struct RustyUi {
    dropdown: gtk::ComboBoxText,
    pitch_label: gtk::Label,
    pitch_error_indicator: gtk::DrawingArea,
    oscilloscope_chart: gtk::DrawingArea,
    correlation_chart: gtk::DrawingArea,
    oscilloscope_toggle_button: gtk::Button,
    correlation_toggle_button: gtk::Button
}

struct ApplicationState {
    pa: pa::PortAudio,
    pa_stream: Option<pa::Stream<pa::NonBlocking, pa::Input<f32>>>,
    ui: RustyUi
}

struct CrossThreadState {
    fundamental_frequency: Option<f32>,
    pitch: String,
    error: Option<f32>,
    signal: Vec<f32>,
    correlation: Vec<f32>
}

pub fn start_gui() -> Result<(), String> {
    let pa = try!(::audio::init().map_err(|e| e.to_string()));
    let microphones = try!(::audio::get_device_list(&pa).map_err(|e| e.to_string()));
    let default_microphone = try!(::audio::get_default_device(&pa).map_err(|e| e.to_string()));

    try!(gtk::init().map_err(|_| "Failed to initialize GTK."));

    let state = Rc::new(RefCell::new(ApplicationState {
        pa: pa,
        pa_stream: None,
        ui: create_window(microphones, default_microphone)
    }));

    let cross_thread_state = Arc::new(RwLock::new(CrossThreadState {
        fundamental_frequency: None,
        pitch: String::new(),
        error: None,
        signal: Vec::new(),
        correlation: Vec::new()
    }));

    let (mic_sender, mic_receiver) = channel();

    connect_dropdown_choose_microphone(mic_sender, state.clone());

    start_processing_audio(mic_receiver, cross_thread_state.clone());
    setup_pitch_label_callbacks(state.clone(), cross_thread_state.clone());
    setup_pitch_error_indicator_callbacks(state.clone(), cross_thread_state.clone());
    setup_oscilloscope_drawing_area_callbacks(state.clone(), cross_thread_state.clone());
    setup_correlation_drawing_area_callbacks(state.clone(), cross_thread_state.clone());

    setup_chart_visibility_callbacks(state.clone());

    gtk::main();
    Ok(())
}

fn create_window(microphones: Vec<(u32, String)>, default_microphone: u32) -> RustyUi {
    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    window.set_title("Rusty Microphone");
    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    let vbox = gtk::Box::new(gtk::Orientation::Vertical, 5);
    window.add(&vbox);

    let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 2);
    vbox.add(&hbox);
    let dropdown = gtk::ComboBoxText::new();
    dropdown.set_hexpand(true);
    set_dropdown_items(&dropdown, microphones, default_microphone);
    hbox.add(&dropdown);

    let oscilloscope_toggle_button = gtk::Button::new_with_label("Osc");
    hbox.add(&oscilloscope_toggle_button);
    let correlation_toggle_button = gtk::Button::new_with_label("Corr");
    hbox.add(&correlation_toggle_button);

    let pitch_label = gtk::Label::new(None);
    vbox.add(&pitch_label);

    let pitch_error_indicator = gtk::DrawingArea::new();
    pitch_error_indicator.set_size_request(600, 70);
    vbox.add(&pitch_error_indicator);

    let oscilloscope_chart = gtk::DrawingArea::new();
    oscilloscope_chart.set_size_request(600, 250);
    oscilloscope_chart.set_vexpand(true);
    vbox.add(&oscilloscope_chart);

    let correlation_chart = gtk::DrawingArea::new();
    correlation_chart.set_size_request(600, 250);
    correlation_chart.set_vexpand(true);
    vbox.add(&correlation_chart);

    window.show_all();

    // correlation chart is only really useful for debugging, so it
    // makes sense to have it default to being hidden
    correlation_chart.set_visible(false);

    RustyUi {
        dropdown: dropdown,
        pitch_label: pitch_label,
        pitch_error_indicator: pitch_error_indicator,
        oscilloscope_chart: oscilloscope_chart,
        correlation_chart: correlation_chart,
        oscilloscope_toggle_button: oscilloscope_toggle_button,
        correlation_toggle_button: correlation_toggle_button
    }
}

fn set_dropdown_items(dropdown: &gtk::ComboBoxText, microphones: Vec<(u32, String)>, default_mic: u32) {
    for (index, name) in microphones {
        dropdown.append(Some(format!("{}", index).as_ref()), name.as_ref());
    }
    dropdown.set_active_id(Some(format!("{}", default_mic).as_ref()));
}

fn connect_dropdown_choose_microphone(mic_sender: Sender<Vec<f32>>, state: Rc<RefCell<ApplicationState>>) {
    let dropdown = state.borrow().ui.dropdown.clone();
    start_listening_current_dropdown_value(&dropdown, mic_sender.clone(), state.clone());
    dropdown.connect_changed(move |dropdown: &gtk::ComboBoxText| {
        start_listening_current_dropdown_value(&dropdown, mic_sender.clone(), state.clone())
    });
}

fn start_listening_current_dropdown_value(dropdown: &gtk::ComboBoxText, mic_sender: Sender<Vec<f32>>, state: Rc<RefCell<ApplicationState>>) {
    match state.borrow_mut().pa_stream {
        Some(ref mut stream) => {stream.stop().ok();},
        _ => {}
    }
    let selected_mic = match dropdown.get_active_id().and_then(|id| id.parse().ok()) {
        Some(mic) => mic,
        None => {return;}
    };
    let stream = ::audio::start_listening(&state.borrow().pa, selected_mic, mic_sender).ok();
    if stream.is_none() {
        writeln!(io::stderr(), "Failed to open audio channel").ok();
    }
    state.borrow_mut().pa_stream = stream;
}

fn start_processing_audio(mic_receiver: Receiver<Vec<f32>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
    thread::spawn(move || {
        loop {
            let mut samples = None;

            // ignore all pending samples until we get to the most recent one
            while let Ok(next_samples) = mic_receiver.try_recv() {
                samples = Some(next_samples);
            }
            let samples = match samples {
                Some(samples) => samples,
                None => {continue;}
            };

            let signal = ::transforms::align_to_rising_edge(&samples);
            let correlation = ::transforms::correlation(&samples);
            let fundamental = ::transforms::find_fundamental_frequency_correlation(&samples, ::audio::SAMPLE_RATE);
            let pitch = match fundamental {
                Some(fundamental) => ::transforms::hz_to_pitch(fundamental),
                None => String::new()
            };
            let error = fundamental.map(::transforms::hz_to_cents_error);

            match cross_thread_state.write() {
                Ok(mut state) => {
                    state.fundamental_frequency = fundamental;
                    state.pitch = pitch;
                    state.signal = signal;
                    state.correlation = correlation;
                    state.error = error;
                },
                Err(err) => {
                    println!("Error updating cross thread state: {}", err);
                }
            };
        }
    });
}

fn setup_pitch_label_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
    gtk::timeout_add(1000/FPS, move || {
        let ref ui = state.borrow().ui;
        if let Ok(cross_thread_state) = cross_thread_state.read() {
            let ref pitch = cross_thread_state.pitch;
            ui.pitch_label.set_label(pitch.as_ref());
            ui.pitch_error_indicator.queue_draw();
            ui.oscilloscope_chart.queue_draw();
            ui.correlation_chart.queue_draw();
        }

        gtk::Continue(true)
    });
}

fn setup_pitch_error_indicator_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
    let outer_state = state.clone();
    let ref canvas = outer_state.borrow().ui.pitch_error_indicator;
    canvas.connect_draw(move |ref canvas, ref context| {
        let width = canvas.get_allocated_width() as f64;
        let midpoint = width / 2.0;

        let line_indicator_height = 20.0;
        let color_indicator_height = canvas.get_allocated_height() as f64 - line_indicator_height;

        match cross_thread_state.read().map(|state| state.error) {
            Ok(Some(error)) =>  {
                let error_line_x = midpoint + error as f64 * midpoint / 50.0;
                context.new_path();
                context.move_to(error_line_x, 0.0);
                context.line_to(error_line_x, line_indicator_height);
                context.stroke();

                //flat on the left
                context.set_source_rgb(0.0, 0.0, if error < 0.0 {-error as f64/50.0} else {0.0});
                context.rectangle(0.0, line_indicator_height, midpoint, color_indicator_height+line_indicator_height);
                context.fill();

                //sharp on the right
                context.set_source_rgb(if error > 0.0 {error as f64/50.0} else {0.0}, 0.0, 0.0);
                context.rectangle(midpoint, line_indicator_height, width, color_indicator_height+line_indicator_height);
                context.fill();
            },
            _ => {
                context.set_source_rgb(0.0, 0.0, 0.0);
                context.rectangle(0.0, line_indicator_height, width, color_indicator_height+line_indicator_height);
                context.fill();
            }
        }

        gtk::Inhibit(false)
    });
}

fn setup_oscilloscope_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
    let outer_state = state.clone();
    let ref canvas = outer_state.borrow().ui.oscilloscope_chart;
    canvas.connect_draw(move |ref canvas, ref context| {
        if let Ok(cross_thread_state) = cross_thread_state.read() {
            let ref signal = cross_thread_state.signal;
            let width = canvas.get_allocated_width() as f64;
            let len = 512.0; //Set as a constant so signal won't change size based on zero point.
            let height = canvas.get_allocated_height() as f64;
            let mid_height = height / 2.0;
            let max = 1.0;

            context.new_path();
            context.move_to(0.0, mid_height);

            for (i, &intensity) in signal.iter().enumerate() {
                let x = i as f64 * width / len;
                let y = mid_height - (intensity as f64 * mid_height / max);
                context.line_to(x, y);
            }

            context.stroke();
        }

        gtk::Inhibit(false)
    });
}

fn setup_correlation_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
    let outer_state = state.clone();
    let ref canvas = outer_state.borrow().ui.correlation_chart;
    canvas.connect_draw(move |ref canvas, ref context| {
        let width = canvas.get_allocated_width() as f64;
        let height = canvas.get_allocated_height() as f64;

        //draw zero
        context.new_path();
        context.move_to(0.0, height/2.0);
        context.line_to(width, height/2.0);
        context.stroke();

        if let Ok(cross_thread_state) = cross_thread_state.read() {
            let ref correlation = cross_thread_state.correlation;
            let len = correlation.len() as f64;
            let max = match correlation.first() {
                Some(&c) => c as f64,
                None => 1.0
            };

            context.new_path();
            context.move_to(0.0, height);
            for (i, &val) in correlation.iter().enumerate() {
                let x = i as f64 * width / len;
                let y = height/2.0 - (val as f64 * height / max / 2.0);
                context.line_to(x, y);
            }
            context.stroke();

            //draw the fundamental
            if let Some(fundamental) = cross_thread_state.fundamental_frequency {
                context.new_path();
                let fundamental_x = ::audio::SAMPLE_RATE as f64 / fundamental as f64 * width / len;
                context.move_to(fundamental_x, 0.0);
                context.line_to(fundamental_x, height);
                context.stroke();
            }
        }

        gtk::Inhibit(false)
    });
}

fn setup_chart_visibility_callbacks(state: Rc<RefCell<ApplicationState>>) {
    let outer_state = state.clone();
    let ref oscilloscope_toggle_button = outer_state.borrow().ui.oscilloscope_toggle_button;
    let ref correlation_toggle_button = outer_state.borrow().ui.correlation_toggle_button;

    let oscilloscope_state = state.clone();
    oscilloscope_toggle_button.connect_clicked(move |_| {
        let ref chart = oscilloscope_state.borrow().ui.oscilloscope_chart;
        chart.set_visible(!chart.get_visible());
    });

    let correlation_state = state.clone();
    correlation_toggle_button.connect_clicked(move |_| {
        let ref chart = correlation_state.borrow().ui.correlation_chart;
        chart.set_visible(!chart.get_visible());
    });
}
<commit_msg>Simplified loop for doing calculations from microphone samples<commit_after>use gtk;
use gtk::prelude::*;
use std::cell::RefCell;
use portaudio as pa;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::RwLock;
use std::io;
use std::io::Write;
use std::thread;
use std::sync::mpsc::*;

const FPS: u32 = 30;

struct RustyUi {
    dropdown: gtk::ComboBoxText,
    pitch_label: gtk::Label,
    pitch_error_indicator: gtk::DrawingArea,
    oscilloscope_chart: gtk::DrawingArea,
    correlation_chart: gtk::DrawingArea,
    oscilloscope_toggle_button: gtk::Button,
    correlation_toggle_button: gtk::Button
}

struct ApplicationState {
    pa: pa::PortAudio,
    pa_stream: Option<pa::Stream<pa::NonBlocking, pa::Input<f32>>>,
    ui: RustyUi
}

struct CrossThreadState {
    fundamental_frequency: Option<f32>,
    pitch: String,
    error: Option<f32>,
    signal: Vec<f32>,
    correlation: Vec<f32>
}

pub fn start_gui() -> Result<(), String> {
    let pa = try!(::audio::init().map_err(|e| e.to_string()));
    let microphones = try!(::audio::get_device_list(&pa).map_err(|e| e.to_string()));
    let default_microphone = try!(::audio::get_default_device(&pa).map_err(|e| e.to_string()));

    try!(gtk::init().map_err(|_| "Failed to initialize GTK."));

    let state = Rc::new(RefCell::new(ApplicationState {
        pa: pa,
        pa_stream: None,
        ui: create_window(microphones, default_microphone)
    }));

    let cross_thread_state = Arc::new(RwLock::new(CrossThreadState {
        fundamental_frequency: None,
        pitch: String::new(),
        error: None,
        signal: Vec::new(),
        correlation: Vec::new()
    }));

    let (mic_sender, mic_receiver) = channel();

    connect_dropdown_choose_microphone(mic_sender, state.clone());

    start_processing_audio(mic_receiver, cross_thread_state.clone());
    setup_pitch_label_callbacks(state.clone(), cross_thread_state.clone());
    setup_pitch_error_indicator_callbacks(state.clone(), cross_thread_state.clone());
    setup_oscilloscope_drawing_area_callbacks(state.clone(), cross_thread_state.clone());
    setup_correlation_drawing_area_callbacks(state.clone(), cross_thread_state.clone());

    setup_chart_visibility_callbacks(state.clone());

    gtk::main();
    Ok(())
}

fn create_window(microphones: Vec<(u32, String)>, default_microphone: u32) -> RustyUi {
    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    window.set_title("Rusty Microphone");
    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    let vbox = gtk::Box::new(gtk::Orientation::Vertical, 5);
    window.add(&vbox);

    let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 2);
    vbox.add(&hbox);
    let dropdown = gtk::ComboBoxText::new();
    dropdown.set_hexpand(true);
    set_dropdown_items(&dropdown, microphones, default_microphone);
    hbox.add(&dropdown);

    let oscilloscope_toggle_button = gtk::Button::new_with_label("Osc");
    hbox.add(&oscilloscope_toggle_button);
    let correlation_toggle_button = gtk::Button::new_with_label("Corr");
    hbox.add(&correlation_toggle_button);

    let pitch_label = gtk::Label::new(None);
    vbox.add(&pitch_label);

    let pitch_error_indicator = gtk::DrawingArea::new();
    pitch_error_indicator.set_size_request(600, 70);
    vbox.add(&pitch_error_indicator);

    let oscilloscope_chart = gtk::DrawingArea::new();
    oscilloscope_chart.set_size_request(600, 250);
    oscilloscope_chart.set_vexpand(true);
    vbox.add(&oscilloscope_chart);

    let correlation_chart = gtk::DrawingArea::new();
    correlation_chart.set_size_request(600, 250);
    correlation_chart.set_vexpand(true);
    vbox.add(&correlation_chart);

    window.show_all();

    // correlation chart is only really useful for debugging, so it
    // makes sense to have it default to being hidden
    correlation_chart.set_visible(false);

    RustyUi {
        dropdown: dropdown,
        pitch_label: pitch_label,
        pitch_error_indicator: pitch_error_indicator,
        oscilloscope_chart: oscilloscope_chart,
        correlation_chart: correlation_chart,
        oscilloscope_toggle_button: oscilloscope_toggle_button,
        correlation_toggle_button: correlation_toggle_button
    }
}

fn set_dropdown_items(dropdown: &gtk::ComboBoxText, microphones: Vec<(u32, String)>, default_mic: u32) {
    for (index, name) in microphones {
        dropdown.append(Some(format!("{}", index).as_ref()), name.as_ref());
    }
    dropdown.set_active_id(Some(format!("{}", default_mic).as_ref()));
}

fn connect_dropdown_choose_microphone(mic_sender: Sender<Vec<f32>>, state: Rc<RefCell<ApplicationState>>) {
    let dropdown = state.borrow().ui.dropdown.clone();
    start_listening_current_dropdown_value(&dropdown, mic_sender.clone(), state.clone());
    dropdown.connect_changed(move |dropdown: &gtk::ComboBoxText| {
        start_listening_current_dropdown_value(&dropdown, mic_sender.clone(), state.clone())
    });
}

fn start_listening_current_dropdown_value(dropdown: &gtk::ComboBoxText, mic_sender: Sender<Vec<f32>>, state: Rc<RefCell<ApplicationState>>) {
    match state.borrow_mut().pa_stream {
        Some(ref mut stream) => {stream.stop().ok();},
        _ => {}
    }
    let selected_mic = match dropdown.get_active_id().and_then(|id| id.parse().ok()) {
        Some(mic) => mic,
        None => {return;}
    };
    let stream = ::audio::start_listening(&state.borrow().pa, selected_mic, mic_sender).ok();
    if stream.is_none() {
        writeln!(io::stderr(), "Failed to open audio channel").ok();
    }
    state.borrow_mut().pa_stream = stream;
}

fn start_processing_audio(mic_receiver: Receiver<Vec<f32>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
    thread::spawn(move || {
        while let Ok(samples) = mic_receiver.recv() {
            let signal = ::transforms::align_to_rising_edge(&samples);
            let correlation = ::transforms::correlation(&samples);
            let fundamental = ::transforms::find_fundamental_frequency_correlation(&samples, ::audio::SAMPLE_RATE);
            let pitch = match fundamental {
                Some(fundamental) => ::transforms::hz_to_pitch(fundamental),
                None => String::new()
            };
            let error = fundamental.map(::transforms::hz_to_cents_error);

            match cross_thread_state.write() {
                Ok(mut state) => {
                    state.fundamental_frequency = fundamental;
                    state.pitch = pitch;
                    state.signal = signal;
                    state.correlation = correlation;
                    state.error = error;
                },
                Err(err) => {
                    println!("Error updating cross thread state: {}", err);
                }
            };
        }
    });
}

fn setup_pitch_label_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
    gtk::timeout_add(1000/FPS, move || {
        let ref ui = state.borrow().ui;
        if let Ok(cross_thread_state) = cross_thread_state.read() {
            let ref pitch = cross_thread_state.pitch;
            ui.pitch_label.set_label(pitch.as_ref());
            ui.pitch_error_indicator.queue_draw();
            ui.oscilloscope_chart.queue_draw();
            ui.correlation_chart.queue_draw();
        }

        gtk::Continue(true)
    });
}

fn setup_pitch_error_indicator_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
    let outer_state = state.clone();
    let ref canvas = outer_state.borrow().ui.pitch_error_indicator;
    canvas.connect_draw(move |ref canvas, ref context| {
        let width = canvas.get_allocated_width() as f64;
        let midpoint = width / 2.0;

        let line_indicator_height = 20.0;
        let color_indicator_height = canvas.get_allocated_height() as f64 - line_indicator_height;

        match cross_thread_state.read().map(|state| state.error) {
            Ok(Some(error)) =>  {
                let error_line_x = midpoint + error as f64 * midpoint / 50.0;
                context.new_path();
                context.move_to(error_line_x, 0.0);
                context.line_to(error_line_x, line_indicator_height);
                context.stroke();

                //flat on the left
                context.set_source_rgb(0.0, 0.0, if error < 0.0 {-error as f64/50.0} else {0.0});
                context.rectangle(0.0, line_indicator_height, midpoint, color_indicator_height+line_indicator_height);
                context.fill();

                //sharp on the right
                context.set_source_rgb(if error > 0.0 {error as f64/50.0} else {0.0}, 0.0, 0.0);
                context.rectangle(midpoint, line_indicator_height, width, color_indicator_height+line_indicator_height);
                context.fill();
            },
            _ => {
                context.set_source_rgb(0.0, 0.0, 0.0);
                context.rectangle(0.0, line_indicator_height, width, color_indicator_height+line_indicator_height);
                context.fill();
            }
        }

        gtk::Inhibit(false)
    });
}

fn setup_oscilloscope_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
    let outer_state = state.clone();
    let ref canvas = outer_state.borrow().ui.oscilloscope_chart;
    canvas.connect_draw(move |ref canvas, ref context| {
        if let Ok(cross_thread_state) = cross_thread_state.read() {
            let ref signal = cross_thread_state.signal;
            let width = canvas.get_allocated_width() as f64;
            let len = 512.0; //Set as a constant so signal won't change size based on zero point.
            let height = canvas.get_allocated_height() as f64;
            let mid_height = height / 2.0;
            let max = 1.0;

            context.new_path();
            context.move_to(0.0, mid_height);

            for (i, &intensity) in signal.iter().enumerate() {
                let x = i as f64 * width / len;
                let y = mid_height - (intensity as f64 * mid_height / max);
                context.line_to(x, y);
            }

            context.stroke();
        }

        gtk::Inhibit(false)
    });
}

fn setup_correlation_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
    let outer_state = state.clone();
    let ref canvas = outer_state.borrow().ui.correlation_chart;
    canvas.connect_draw(move |ref canvas, ref context| {
        let width = canvas.get_allocated_width() as f64;
        let height = canvas.get_allocated_height() as f64;

        //draw zero
        context.new_path();
        context.move_to(0.0, height/2.0);
        context.line_to(width, height/2.0);
        context.stroke();

        if let Ok(cross_thread_state) = cross_thread_state.read() {
            let ref correlation = cross_thread_state.correlation;
            let len = correlation.len() as f64;
            let max = match correlation.first() {
                Some(&c) => c as f64,
                None => 1.0
            };

            context.new_path();
            context.move_to(0.0, height);
            for (i, &val) in correlation.iter().enumerate() {
                let x = i as f64 * width / len;
                let y = height/2.0 - (val as f64 * height / max / 2.0);
                context.line_to(x, y);
            }
            context.stroke();

            //draw the fundamental
            if let Some(fundamental) = cross_thread_state.fundamental_frequency {
                context.new_path();
                let fundamental_x = ::audio::SAMPLE_RATE as f64 / fundamental as f64 * width / len;
                context.move_to(fundamental_x, 0.0);
                context.line_to(fundamental_x, height);
                context.stroke();
            }
        }

        gtk::Inhibit(false)
    });
}

fn setup_chart_visibility_callbacks(state: Rc<RefCell<ApplicationState>>) {
    let outer_state = state.clone();
    let ref oscilloscope_toggle_button = outer_state.borrow().ui.oscilloscope_toggle_button;
    let ref correlation_toggle_button = outer_state.borrow().ui.correlation_toggle_button;

    let oscilloscope_state = state.clone();
    oscilloscope_toggle_button.connect_clicked(move |_| {
        let ref chart = oscilloscope_state.borrow().ui.oscilloscope_chart;
        chart.set_visible(!chart.get_visible());
    });

    let correlation_state = state.clone();
    correlation_toggle_button.connect_clicked(move |_| {
        let ref chart = correlation_state.borrow().ui.correlation_chart;
        chart.set_visible(!chart.get_visible());
    });
}
<|endoftext|>