<commit_before>use crate::Msg;
use seed::prelude::*;
use web_sys::{HtmlInputElement};

fn trial_divide(n: u64, max: u64) -> u64 {
    // Trial divides the positive integer n by the primes from 2 to max
    // Returns the first prime divisor found, or 0 if none found
    // Note: if n < max^2 is a prime, then n will be returned.
    if n % 2 == 0 {
        2
    } else if n % 3 == 0 {
        3
    } else {
        // No need to go past the square root of our number
        let sqrt: u64 = (n as f64).sqrt().floor() as u64;
        let stop = if sqrt > max { max } else { sqrt };
        // Okay, lets "wheel factor" alternately adding 2 and 4
        let mut di = 2;
        let mut i = 5;
        while i <= stop {
            if n % i == 0 {
                return i;
            }
            i += di;
            di = 6 - di;
        }
        if n >= max * max {
            0
        } else {
            n
        }
    }
}

// modadd(a,b,N) finds a+b (mod N) where a, b, and N can be
// up to (2^53-1)/2.  Might up this to 2^53-1 eventually...
fn modadd(a: u64, b: u64, n: u64) -> u64 {
    // When the integers a, b satisfy a+b > 2^53-1, then (a+b)%N is wrong
    // so we add this routine to allow us to reach a, b = 2^53-1.
    if a + b > 9007199254740991 {
        // Could reduce a and b (mod N) here, but assuming that has already been done
        // won't hurt if not... subtract 2^52 from one, 2^52-1 from the other and the
        // add it back modulo N (MaxInt+1)
        let t = ((a - 4503599627370496) + (b - 4503599627370495)) % n;
        return t + (9007199254740991 % n);
    }
    // Usual case: a + b is not too large:
    (a + b) % n
}

fn modmult(mut a: u64, mut b: u64, n: u64) -> u64 {
    if a > n {
        a = a % n;
    }
    if b > n {
        b = b % n;
    }
    if a * b <= 9007199254740991 {
        return (a * b) % n;
    } else {
        if b > a {
            return modmult(b.clone(), a, n);
        }

        // Right to left binary multiplication
        let mut t = 0;
        let mut f = a;
        while b > 1 {
            if (b & 1) == 1 {
                t = modadd(t, f, n);
            }
            //TODO: bitshift might be cleaner and faster
            b = (b as f64 / 2.0).floor() as u64;
            f = modadd(f, f, n);
        }
        t = modadd(t, f, n);
        return t;
    }
}

// modpow(a,exp,N) finds a^exp (mod N) where a, b, and N are
// limited by modmult
fn modpow(a: u64, mut exp: u64, n: u64) -> u64 {
    if exp == 0 {
        return 1;
    }

    // Right to left binary exponentiation
    let mut t = 1;
    let mut f = a;
    while exp > 1 {
        if (exp & 1) == 1 {
            // if exponent is odd
            t = modmult(t, f, n);
        }
        exp = (exp as f64 / 2.0).floor() as u64;
        f = modmult(f, f, n);
    }
    t = modmult(t, f, n);
    return t;
}

// sprp(N,a) checks if N (odd!) is a strong probable prime base a
// (returns true or false)
fn sprp(n: u64, a: u64) -> bool {
    let mut d = n - 1;
    let mut s = 1; // Assumes n is odd!
    while true {
        d = ((d >> 2) & 1);
        if d == 0 {
            break;
        }
        s += 1;
    }
    // Now n-1 = d*2^s with d odd
    let mut b = modpow(a, d, n);
    if b == 1 {
        return true;
    }
    if b + 1 == n {
        return true;
    }
    while s > 1 {
        b = modmult(b, b, n);
        if b + 1 == n {
            return true;
        }
        s = s - 1;
    }
    return false;
}

#[wasm_bindgen]
pub fn check(input: String) -> String {
    let trial_limit = 1300; // Should be bigger, like 10000
    let n: u64 = input.parse::<u64>().unwrap();
    let mut result;

    if n > 9007199254740991 {
        result = "Sorry, this routine will only handle integers below 9007199254740991.".to_owned();
    } else if n == 1 {
        result = "The number 1 is neither prime or composite (it is the multiplicative identity)."
            .to_owned();
    } else if n < 1 {
        result =
            "We usually restrict the terms prime and composite to positive integers".to_owned();
    } else {
        // Okay, n is of a resonable size, lets trial divide
        let i = trial_divide(n, trial_limit);
        if i > 0 && i != n {
            result = format!("{} is not a prime! It is {} * {}", n, i, n / i);
        } else if n < trial_limit * trial_limit {
            result = format!("{} is a (proven) prime!", n);
        } else if sprp(n, 2)
            && sprp(n, 3)
            && sprp(n, 5)
            && sprp(n, 7)
            && sprp(n, 11)
            && sprp(n, 13)
            && sprp(n, 17)
        {
            // Some of these tests are unnecessary for small numbers, but for
            // small numbers they are quick anyway.
            if n < 341550071728321 {
                result = format!("{} is a (proven) prime.", n);
            } else if n == 341550071728321 {
                result = format!("{} is not a prime! It is 10670053 * 32010157.", n);
            } else {
                result = format!(
                    "{} is probably a prime (it is a sprp bases 2, 3, 5, 7, 11, 13 and  17).",
                    n
                );
            };
        } else {
            result = format!(
                "{} is (proven) composite (failed sprp test base {}).",
                n, 17
            );
        };
    };

    return result;
}

#[wasm_bindgen]
pub fn listy(start_number: u64, number_of_primes: u64) -> String {
    let mut i = 0;
    let mut j = start_number;
    let mut list: String = "".to_string();
    while i < number_of_primes {
        let result = check(j.to_string());
        if result != "".to_owned() {
            list = list + &result + "\n";
            i += 1;
        }
        j += 1;
    }
    list
}

pub fn go_crunch() -> () {
    let window = web_sys::window().expect("no global `window` exists");
    let document = window.document().expect("should have a document on window");
    let body = document.body().expect("document should have a body");

    let el_input_value = document
        .get_element_by_id("input")
        .unwrap()
        .dyn_into::<HtmlInputElement>()
        .unwrap()
        .value();
    let el_output_textarea = document
        .get_element_by_id("prime_check_output")
        .expect("missing output textarea");
    el_output_textarea.set_inner_html(&check(el_input_value));
    ()
}

pub fn go_list() {
    let window = web_sys::window().expect("no global `window` exists");
    let document = window.document().expect("should have a document on window");
    let body = document.body().expect("document should have a body");

    let el_start_value = document
        .get_element_by_id("start")
        .unwrap()
        .dyn_into::<HtmlInputElement>()
        .unwrap()
        .value();
    let el_primes_value = document
        .get_element_by_id("primes")
        .unwrap()
        .dyn_into::<HtmlInputElement>()
        .unwrap()
        .value();
    let el_output_textarea = document
        .get_element_by_id("prime_check_list_output")
        .expect("missing output textarea");
    el_output_textarea.set_inner_html(&listy(
        el_start_value.parse::<u64>().unwrap(),
        el_primes_value.parse::<u64>().unwrap(),
    ));
    ()
}

pub fn render() -> Node<Msg> {
    div![
        h1!["Primality Test, Calculate Primes"],
        br![],
        br![],
        br![],
        table![
            attrs! {At::Class => "text", At::Width => "300", At::Style => "border:1px solid #444; background-color:#e0faed"},
            tr![td![
                attrs! {At::Style => "padding: 10px"},
                form![
                    attrs! {At::Id => "primetest", At::OnSubmit => "return false"},
                    "Tool is limited to checking numbers upto 16 digits.",
                    br![],
                    br![],
                    "Is ",
                    input![
                        attrs! {At::Type => "number", At::Size => "19", At::Id => "input", At::Value => "", At::MaxLength => "16"}
                    ],
                    " prime? ",
                    button![
                        "Check!",
                        mouse_ev("mouseup", move |event| Msg::PrimalityChecker(go_crunch()))
                    ],
                    br![],
                    br![],
                    textarea![
                        attrs! {At::Id => "prime_check_output", At::Cols => 60, At::Rows => 2, At::Disabled => "disabled"}
                    ]
                ]
            ]]
        ],
        br![],
        br![],
        table![
            attrs! {At::Class => "text", At::Width => "300", At::Style => "border:1px solid #444; background-color:#e0faed"},
            tr![td![
                attrs! {At::Style => "padding: 10px"},
                form![
                    attrs! {At::Id => "primelist", At::OnSubmit => "return false"},
                    "This tool is limited to checking numbers upto 15 digits.",
                    br![],
                    br![],
                    "This will show ",
                    input![
                        attrs! {At::Type => "number", At::Size => "4", At::Id => "primes", At::Value => "1", At::MaxLength => "2"}
                    ],
                    " prime numbers after ",
                    input![
                        attrs! {At::Type => "number", At::Size => "19", At::Id => "start", At::Value => "0", At::MaxLength => "15"}
                    ],
                    " ",
                    button![
                        "Go!",
                        mouse_ev("mouseup", move |event| Msg::PrimalityChecker(go_list()))
                    ],
                    br![],
                    br![],
                    textarea![
                        attrs! {At::Id => "prime_check_list_output", At::Cols => 60, At::Rows => 10, At::Disabled => "disabled"}
                    ]
                ]
            ]]
        ]
    ]
}

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

    #[test]
    fn trial_divide_test() {
        assert_eq!(trial_divide(2, 2), 2);
        assert_eq!(trial_divide(3, 4), 3);
        assert_eq!(trial_divide(30, 10000), 2);
        assert_eq!(trial_divide(7777771, 10000), 29);
        assert_eq!(trial_divide(7777771111111111, 10000), 11);
        //in the case of a prime
        assert_eq!(trial_divide(777777111111113, 10000), 0);
    }

    #[test]
    fn sprp_test() {
        assert_eq!(sprp(27, 5), false);
        assert_eq!(sprp(31, 7), true);
        assert_eq!(sprp(217, 7), false);
        assert_eq!(sprp(19, 13), true);
    }
}
<commit_msg>prefixed unused event to _event<commit_after>use crate::Msg;
use seed::prelude::*;
use web_sys::{HtmlInputElement};

fn trial_divide(n: u64, max: u64) -> u64 {
    // Trial divides the positive integer n by the primes from 2 to max
    // Returns the first prime divisor found, or 0 if none found
    // Note: if n < max^2 is a prime, then n will be returned.
    if n % 2 == 0 {
        2
    } else if n % 3 == 0 {
        3
    } else {
        // No need to go past the square root of our number
        let sqrt: u64 = (n as f64).sqrt().floor() as u64;
        let stop = if sqrt > max { max } else { sqrt };
        // Okay, lets "wheel factor" alternately adding 2 and 4
        let mut di = 2;
        let mut i = 5;
        while i <= stop {
            if n % i == 0 {
                return i;
            }
            i += di;
            di = 6 - di;
        }
        if n >= max * max {
            0
        } else {
            n
        }
    }
}

// modadd(a,b,N) finds a+b (mod N) where a, b, and N can be
// up to (2^53-1)/2.  Might up this to 2^53-1 eventually...
fn modadd(a: u64, b: u64, n: u64) -> u64 {
    // When the integers a, b satisfy a+b > 2^53-1, then (a+b)%N is wrong
    // so we add this routine to allow us to reach a, b = 2^53-1.
    if a + b > 9007199254740991 {
        // Could reduce a and b (mod N) here, but assuming that has already been done
        // won't hurt if not... subtract 2^52 from one, 2^52-1 from the other and the
        // add it back modulo N (MaxInt+1)
        let t = ((a - 4503599627370496) + (b - 4503599627370495)) % n;
        return t + (9007199254740991 % n);
    }
    // Usual case: a + b is not too large:
    (a + b) % n
}

fn modmult(mut a: u64, mut b: u64, n: u64) -> u64 {
    if a > n {
        a = a % n;
    }
    if b > n {
        b = b % n;
    }
    if a * b <= 9007199254740991 {
        return (a * b) % n;
    } else {
        if b > a {
            return modmult(b.clone(), a, n);
        }

        // Right to left binary multiplication
        let mut t = 0;
        let mut f = a;
        while b > 1 {
            if (b & 1) == 1 {
                t = modadd(t, f, n);
            }
            //TODO: bitshift might be cleaner and faster
            b = (b as f64 / 2.0).floor() as u64;
            f = modadd(f, f, n);
        }
        t = modadd(t, f, n);
        return t;
    }
}

// modpow(a,exp,N) finds a^exp (mod N) where a, b, and N are
// limited by modmult
fn modpow(a: u64, mut exp: u64, n: u64) -> u64 {
    if exp == 0 {
        return 1;
    }

    // Right to left binary exponentiation
    let mut t = 1;
    let mut f = a;
    while exp > 1 {
        if (exp & 1) == 1 {
            // if exponent is odd
            t = modmult(t, f, n);
        }
        exp = (exp as f64 / 2.0).floor() as u64;
        f = modmult(f, f, n);
    }
    t = modmult(t, f, n);
    return t;
}

// sprp(N,a) checks if N (odd!) is a strong probable prime base a
// (returns true or false)
fn sprp(n: u64, a: u64) -> bool {
    let mut d = n - 1;
    let mut s = 1; // Assumes n is odd!
    while true {
        d = ((d >> 2) & 1);
        if d == 0 {
            break;
        }
        s += 1;
    }
    // Now n-1 = d*2^s with d odd
    let mut b = modpow(a, d, n);
    if b == 1 {
        return true;
    }
    if b + 1 == n {
        return true;
    }
    while s > 1 {
        b = modmult(b, b, n);
        if b + 1 == n {
            return true;
        }
        s = s - 1;
    }
    return false;
}

#[wasm_bindgen]
pub fn check(input: String) -> String {
    let trial_limit = 1300; // Should be bigger, like 10000
    let n: u64 = input.parse::<u64>().unwrap();
    let mut result;

    if n > 9007199254740991 {
        result = "Sorry, this routine will only handle integers below 9007199254740991.".to_owned();
    } else if n == 1 {
        result = "The number 1 is neither prime or composite (it is the multiplicative identity)."
            .to_owned();
    } else if n < 1 {
        result =
            "We usually restrict the terms prime and composite to positive integers".to_owned();
    } else {
        // Okay, n is of a resonable size, lets trial divide
        let i = trial_divide(n, trial_limit);
        if i > 0 && i != n {
            result = format!("{} is not a prime! It is {} * {}", n, i, n / i);
        } else if n < trial_limit * trial_limit {
            result = format!("{} is a (proven) prime!", n);
        } else if sprp(n, 2)
            && sprp(n, 3)
            && sprp(n, 5)
            && sprp(n, 7)
            && sprp(n, 11)
            && sprp(n, 13)
            && sprp(n, 17)
        {
            // Some of these tests are unnecessary for small numbers, but for
            // small numbers they are quick anyway.
            if n < 341550071728321 {
                result = format!("{} is a (proven) prime.", n);
            } else if n == 341550071728321 {
                result = format!("{} is not a prime! It is 10670053 * 32010157.", n);
            } else {
                result = format!(
                    "{} is probably a prime (it is a sprp bases 2, 3, 5, 7, 11, 13 and  17).",
                    n
                );
            };
        } else {
            result = format!(
                "{} is (proven) composite (failed sprp test base {}).",
                n, 17
            );
        };
    };

    return result;
}

#[wasm_bindgen]
pub fn listy(start_number: u64, number_of_primes: u64) -> String {
    let mut i = 0;
    let mut j = start_number;
    let mut list: String = "".to_string();
    while i < number_of_primes {
        let result = check(j.to_string());
        if result != "".to_owned() {
            list = list + &result + "\n";
            i += 1;
        }
        j += 1;
    }
    list
}

pub fn go_crunch() -> () {
    let window = web_sys::window().expect("no global `window` exists");
    let document = window.document().expect("should have a document on window");
    let body = document.body().expect("document should have a body");

    let el_input_value = document
        .get_element_by_id("input")
        .unwrap()
        .dyn_into::<HtmlInputElement>()
        .unwrap()
        .value();
    let el_output_textarea = document
        .get_element_by_id("prime_check_output")
        .expect("missing output textarea");
    el_output_textarea.set_inner_html(&check(el_input_value));
    ()
}

pub fn go_list() {
    let window = web_sys::window().expect("no global `window` exists");
    let document = window.document().expect("should have a document on window");
    let body = document.body().expect("document should have a body");

    let el_start_value = document
        .get_element_by_id("start")
        .unwrap()
        .dyn_into::<HtmlInputElement>()
        .unwrap()
        .value();
    let el_primes_value = document
        .get_element_by_id("primes")
        .unwrap()
        .dyn_into::<HtmlInputElement>()
        .unwrap()
        .value();
    let el_output_textarea = document
        .get_element_by_id("prime_check_list_output")
        .expect("missing output textarea");
    el_output_textarea.set_inner_html(&listy(
        el_start_value.parse::<u64>().unwrap(),
        el_primes_value.parse::<u64>().unwrap(),
    ));
    ()
}

pub fn render() -> Node<Msg> {
    div![
        h1!["Primality Test, Calculate Primes"],
        br![],
        br![],
        br![],
        table![
            attrs! {At::Class => "text", At::Width => "300", At::Style => "border:1px solid #444; background-color:#e0faed"},
            tr![td![
                attrs! {At::Style => "padding: 10px"},
                form![
                    attrs! {At::Id => "primetest", At::OnSubmit => "return false"},
                    "Tool is limited to checking numbers upto 16 digits.",
                    br![],
                    br![],
                    "Is ",
                    input![
                        attrs! {At::Type => "number", At::Size => "19", At::Id => "input", At::Value => "", At::MaxLength => "16"}
                    ],
                    " prime? ",
                    button![
                        "Check!",
                        mouse_ev("mouseup", move |_event| Msg::PrimalityChecker(go_crunch()))
                    ],
                    br![],
                    br![],
                    textarea![
                        attrs! {At::Id => "prime_check_output", At::Cols => 60, At::Rows => 2, At::Disabled => "disabled"}
                    ]
                ]
            ]]
        ],
        br![],
        br![],
        table![
            attrs! {At::Class => "text", At::Width => "300", At::Style => "border:1px solid #444; background-color:#e0faed"},
            tr![td![
                attrs! {At::Style => "padding: 10px"},
                form![
                    attrs! {At::Id => "primelist", At::OnSubmit => "return false"},
                    "This tool is limited to checking numbers upto 15 digits.",
                    br![],
                    br![],
                    "This will show ",
                    input![
                        attrs! {At::Type => "number", At::Size => "4", At::Id => "primes", At::Value => "1", At::MaxLength => "2"}
                    ],
                    " prime numbers after ",
                    input![
                        attrs! {At::Type => "number", At::Size => "19", At::Id => "start", At::Value => "0", At::MaxLength => "15"}
                    ],
                    " ",
                    button![
                        "Go!",
                        mouse_ev("mouseup", move |_event| Msg::PrimalityChecker(go_list()))
                    ],
                    br![],
                    br![],
                    textarea![
                        attrs! {At::Id => "prime_check_list_output", At::Cols => 60, At::Rows => 10, At::Disabled => "disabled"}
                    ]
                ]
            ]]
        ]
    ]
}

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

    #[test]
    fn trial_divide_test() {
        assert_eq!(trial_divide(2, 2), 2);
        assert_eq!(trial_divide(3, 4), 3);
        assert_eq!(trial_divide(30, 10000), 2);
        assert_eq!(trial_divide(7777771, 10000), 29);
        assert_eq!(trial_divide(7777771111111111, 10000), 11);
        //in the case of a prime
        assert_eq!(trial_divide(777777111111113, 10000), 0);
    }

    #[test]
    fn sprp_test() {
        assert_eq!(sprp(27, 5), false);
        assert_eq!(sprp(31, 7), true);
        assert_eq!(sprp(217, 7), false);
        assert_eq!(sprp(19, 13), true);
    }
}
<|endoftext|>


<commit_before><commit_msg>Improve docs<commit_after><|endoftext|>


<commit_before>use std::{iter, thread};
use std::sync::atomic::{AtomicIsize, AtomicUsize, AtomicBool, Ordering};
use comm;
use config;
use player::Player;
use field::{Pos, Field};
use trajectories_pruning::TrajectoriesPruning;
use common;

const MINIMAX_STR: &'static str = "minimax";

fn alpha_beta(field: &mut Field, depth: u32, last_pos: Pos, player: Player, trajectories_pruning: &TrajectoriesPruning, mut alpha: i32, beta: i32, empty_board: &mut Vec<u32>) -> i32 {
  let enemy = player.next();
  if common::is_last_move_stupid(field, last_pos, enemy) {
    return i32::max_value();
  }
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  if moves.is_empty() {
    return field.score(player);
  }
  for pos in moves {
    field.put_point(pos, player);
    if common::is_penult_move_stuped(field) {
      field.undo();
      return i32::max_value();
    }
    let next_trajectories_pruning = TrajectoriesPruning::from_last(field, enemy, depth - 1, empty_board, trajectories_pruning, pos);
    let mut cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -alpha - 1, -alpha, empty_board);
    if cur_estimation > alpha && cur_estimation < beta {
      cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, empty_board);
    }
    field.undo();
    if cur_estimation > alpha {
      alpha = cur_estimation;
      if alpha >= beta {
        break;
      }
    }
  }
  alpha
}

fn alpha_beta_parallel(field: &mut Field, player: Player, depth: u32, alpha: i32, beta: i32, trajectories_pruning: &TrajectoriesPruning, empty_board: &mut Vec<u32>, best_move: &mut Option<Pos>, should_stop: &AtomicBool) -> i32 {
  info!(target: MINIMAX_STR, "Starting parellel alpha beta with depth {}, player {} and beta {}.", depth, player, beta);
  if depth == 0 || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  debug!(target: MINIMAX_STR, "Moves in consideration: {:?}.", moves.iter().map(|&pos| (field.to_x(pos), field.to_y(pos))).collect::<Vec<(u32, u32)>>());
  if moves.is_empty() || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let (producer, consumer) = comm::spmc::unbounded::new();
  for pos in moves {
    producer.send(pos).ok();
  }
  let threads_count = config::threads_count();
  let atomic_alpha = AtomicIsize::new(alpha as isize);
  let atomic_best_move = AtomicUsize::new(0);
  let mut guards = Vec::with_capacity(threads_count);
    for _ in 0 .. threads_count {
    guards.push(thread::scoped(|| {
      let local_consumer = consumer.clone();
      let mut local_field = field.clone();
      let mut local_empty_board = iter::repeat(0u32).take(field.length()).collect();
      let enemy = player.next();
      while let Some(pos) = local_consumer.recv_async().ok() {
        if should_stop.load(Ordering::Relaxed) {
          debug!(target: MINIMAX_STR, "Time-out!");
          break;
        }
        local_field.put_point(pos, player);
        let next_trajectories_pruning = TrajectoriesPruning::from_last(&mut local_field, enemy, depth - 1, &mut local_empty_board, &trajectories_pruning, pos);
        if should_stop.load(Ordering::Relaxed) {
          debug!(target: MINIMAX_STR, "Time-out!");
          break;
        }
        let cur_alpha = atomic_alpha.load(Ordering::SeqCst) as i32;
        if cur_alpha >= beta {
          break;
        }
        let mut cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -cur_alpha - 1, -cur_alpha, &mut local_empty_board);
        if cur_estimation > cur_alpha {
          if !should_stop.load(Ordering::Relaxed) {
            cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, &mut local_empty_board);
          } else {
            debug!(target: MINIMAX_STR, "Time-out! Next estimation ma be approximated.");
          }
        }
        local_field.undo();
        loop {
          let last_pos = atomic_best_move.load(Ordering::SeqCst);
          let last_alpha = atomic_alpha.load(Ordering::SeqCst);
          if cur_estimation > last_alpha as i32 {
            if atomic_alpha.compare_and_swap(last_alpha, cur_estimation as isize, Ordering::SeqCst) == last_alpha && atomic_best_move.compare_and_swap(last_pos, pos, Ordering::SeqCst) == last_pos {
              debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
              break;
            }
          } else {
            debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation > cur_alpha { if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" } } else { "Upper bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
            break;
          }
        }
      }
    }));
  }
  drop(guards);
  let result = atomic_best_move.load(Ordering::SeqCst);
  if result != 0 {
    info!(target: MINIMAX_STR, "Best move is ({}, {}).", field.to_x(result), field.to_y(result));
    *best_move = Some(result);
  } else {
    info!(target: MINIMAX_STR, "Best move is not found.");
    *best_move = None;
  }
  let cur_alpha = atomic_alpha.load(Ordering::SeqCst);
  info!(target: MINIMAX_STR, "Estimation is {}.", cur_alpha);
  cur_alpha as i32
}

pub fn minimax(field: &mut Field, player: Player, depth: u32) -> Option<Pos> {
  info!(target: MINIMAX_STR, "Starting minimax with depth {} and player {}.", depth, player);
  if depth == 0 {
    return None;
  }
  let should_stop = AtomicBool::new(false);
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  let mut best_move = None;
  info!(target: MINIMAX_STR, "Calculating of our estimation. Player is {}", player);
  let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut best_move, &should_stop);
  let enemy = player.next();
  let mut enemy_best_move = best_move;
  let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
  info!(target: MINIMAX_STR, "Calculating of enemy estimation with upper bound {}. Player is {}", -estimation + 1, enemy);
  if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move, &should_stop) < estimation {
    info!(target: MINIMAX_STR,  "Estimation is greater than enemy estimation. So the best move is {:?}, estimation is {}.", best_move.map(|pos| (field.to_x(pos), field.to_y(pos))), estimation);
    best_move
  } else {
    info!(target: MINIMAX_STR,  "Estimation is less than or equal enemy estimation. So all moves have the same estimation {}.", estimation);
    None
  }
}

pub fn minimax_with_time(field: &mut Field, player: Player, time: u32) -> Option<Pos> {
  let should_stop = AtomicBool::new(false);
  let guard = thread::scoped(|| {
    thread::sleep_ms(time);
    should_stop.store(true, Ordering::Relaxed);
  });
  let enemy = player.next();
  let mut depth = 1;
  let mut best_move = None;
  let mut cur_best_move = None;
  let mut enemy_best_move = None;
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let mut trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  while !should_stop.load(Ordering::Relaxed) {
    let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut cur_best_move, &should_stop);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    best_move = if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move, &should_stop) < estimation || should_stop.load(Ordering::Relaxed) {
      cur_best_move
    } else {
      None
    };
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    depth += 1;
    trajectories_pruning = TrajectoriesPruning::inc_exists(field, player, depth, &mut empty_board, &trajectories_pruning);
  }
  drop(guard);
  best_move
}
<commit_msg>Time control improved.<commit_after>use std::{iter, thread};
use std::sync::atomic::{AtomicIsize, AtomicUsize, AtomicBool, Ordering};
use comm;
use config;
use player::Player;
use field::{Pos, Field};
use trajectories_pruning::TrajectoriesPruning;
use common;

const MINIMAX_STR: &'static str = "minimax";

fn alpha_beta(field: &mut Field, depth: u32, last_pos: Pos, player: Player, trajectories_pruning: &TrajectoriesPruning, mut alpha: i32, beta: i32, empty_board: &mut Vec<u32>, should_stop: &AtomicBool) -> i32 {
  let enemy = player.next();
  if common::is_last_move_stupid(field, last_pos, enemy) {
    return i32::max_value();
  }
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  if moves.is_empty() {
    return field.score(player);
  }
  for pos in moves {
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    field.put_point(pos, player);
    if common::is_penult_move_stuped(field) {
      field.undo();
      return i32::max_value();
    }
    let next_trajectories_pruning = TrajectoriesPruning::from_last(field, enemy, depth - 1, empty_board, trajectories_pruning, pos);
    let mut cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -alpha - 1, -alpha, empty_board, should_stop);
    if cur_estimation > alpha && cur_estimation < beta {
      cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, empty_board, should_stop);
    }
    field.undo();
    if cur_estimation > alpha {
      alpha = cur_estimation;
      if alpha >= beta {
        break;
      }
    }
  }
  alpha
}

fn alpha_beta_parallel(field: &mut Field, player: Player, depth: u32, alpha: i32, beta: i32, trajectories_pruning: &TrajectoriesPruning, empty_board: &mut Vec<u32>, best_move: &mut Option<Pos>, should_stop: &AtomicBool) -> i32 {
  info!(target: MINIMAX_STR, "Starting parellel alpha beta with depth {}, player {} and beta {}.", depth, player, beta);
  if depth == 0 || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  debug!(target: MINIMAX_STR, "Moves in consideration: {:?}.", moves.iter().map(|&pos| (field.to_x(pos), field.to_y(pos))).collect::<Vec<(u32, u32)>>());
  if moves.is_empty() || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let (producer, consumer) = comm::spmc::unbounded::new();
  for pos in moves {
    producer.send(pos).ok();
  }
  let threads_count = config::threads_count();
  let atomic_alpha = AtomicIsize::new(alpha as isize);
  let atomic_best_move = AtomicUsize::new(0);
  let mut guards = Vec::with_capacity(threads_count);
    for _ in 0 .. threads_count {
    guards.push(thread::scoped(|| {
      let local_consumer = consumer.clone();
      let mut local_field = field.clone();
      let mut local_empty_board = iter::repeat(0u32).take(field.length()).collect();
      let enemy = player.next();
      while let Some(pos) = local_consumer.recv_async().ok() {
        if should_stop.load(Ordering::Relaxed) {
          debug!(target: MINIMAX_STR, "Time-out!");
          break;
        }
        local_field.put_point(pos, player);
        let next_trajectories_pruning = TrajectoriesPruning::from_last(&mut local_field, enemy, depth - 1, &mut local_empty_board, &trajectories_pruning, pos);
        if should_stop.load(Ordering::Relaxed) {
          debug!(target: MINIMAX_STR, "Time-out!");
          break;
        }
        let cur_alpha = atomic_alpha.load(Ordering::SeqCst) as i32;
        if cur_alpha >= beta {
          break;
        }
        let mut cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -cur_alpha - 1, -cur_alpha, &mut local_empty_board, should_stop);
        if cur_estimation > cur_alpha {
          if !should_stop.load(Ordering::Relaxed) {
            cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, &mut local_empty_board, should_stop);
          } else {
            debug!(target: MINIMAX_STR, "Time-out! Next estimation ma be approximated.");
          }
        }
        local_field.undo();
        loop {
          let last_pos = atomic_best_move.load(Ordering::SeqCst);
          let last_alpha = atomic_alpha.load(Ordering::SeqCst);
          if cur_estimation > last_alpha as i32 {
            if atomic_alpha.compare_and_swap(last_alpha, cur_estimation as isize, Ordering::SeqCst) == last_alpha && atomic_best_move.compare_and_swap(last_pos, pos, Ordering::SeqCst) == last_pos {
              debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
              break;
            }
          } else {
            debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation > cur_alpha { if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" } } else { "Upper bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
            break;
          }
        }
      }
    }));
  }
  drop(guards);
  let result = atomic_best_move.load(Ordering::SeqCst);
  if result != 0 {
    info!(target: MINIMAX_STR, "Best move is ({}, {}).", field.to_x(result), field.to_y(result));
    *best_move = Some(result);
  } else {
    info!(target: MINIMAX_STR, "Best move is not found.");
    *best_move = None;
  }
  let cur_alpha = atomic_alpha.load(Ordering::SeqCst);
  info!(target: MINIMAX_STR, "Estimation is {}.", cur_alpha);
  cur_alpha as i32
}

pub fn minimax(field: &mut Field, player: Player, depth: u32) -> Option<Pos> {
  info!(target: MINIMAX_STR, "Starting minimax with depth {} and player {}.", depth, player);
  if depth == 0 {
    return None;
  }
  let should_stop = AtomicBool::new(false);
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  let mut best_move = None;
  info!(target: MINIMAX_STR, "Calculating of our estimation. Player is {}", player);
  let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut best_move, &should_stop);
  let enemy = player.next();
  let mut enemy_best_move = best_move;
  let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
  info!(target: MINIMAX_STR, "Calculating of enemy estimation with upper bound {}. Player is {}", -estimation + 1, enemy);
  if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move, &should_stop) < estimation {
    info!(target: MINIMAX_STR,  "Estimation is greater than enemy estimation. So the best move is {:?}, estimation is {}.", best_move.map(|pos| (field.to_x(pos), field.to_y(pos))), estimation);
    best_move
  } else {
    info!(target: MINIMAX_STR,  "Estimation is less than or equal enemy estimation. So all moves have the same estimation {}.", estimation);
    None
  }
}

pub fn minimax_with_time(field: &mut Field, player: Player, time: u32) -> Option<Pos> {
  let should_stop = AtomicBool::new(false);
  let guard = thread::scoped(|| {
    thread::sleep_ms(time);
    should_stop.store(true, Ordering::Relaxed);
  });
  let enemy = player.next();
  let mut depth = 1;
  let mut best_move = None;
  let mut cur_best_move = None;
  let mut enemy_best_move = None;
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let mut trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  while !should_stop.load(Ordering::Relaxed) {
    let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut cur_best_move, &should_stop);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    best_move = if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move, &should_stop) < estimation || should_stop.load(Ordering::Relaxed) {
      cur_best_move
    } else {
      None
    };
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    depth += 1;
    trajectories_pruning = TrajectoriesPruning::inc_exists(field, player, depth, &mut empty_board, &trajectories_pruning);
  }
  drop(guard);
  best_move
}
<|endoftext|>


<commit_before>
use super::{Github, Result};
use users::User;

/// A structure for interfacing with a issue comments
pub struct ReviewComments<'a> {
    github: &'a Github,
    owner: String,
    repo: String,
    number: u64,
}

impl<'a> ReviewComments<'a> {
    pub fn new<O, R>(github: &'a Github, owner: O, repo: R, number: u64) -> ReviewComments<'a>
        where O: Into<String>,
              R: Into<String>
    {
        ReviewComments {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
            number: number,
        }
    }

    /// list pull requests
    pub fn list(&self) -> Result<Vec<ReviewComment>> {
        self.github.get::<Vec<ReviewComment>>(&format!("/repos/{}/{}/pulls/{}/comments",
                                                       self.owner,
                                                       self.repo,
                                                       self.number))
    }
}

// representations

#[derive(Debug, Deserialize)]
pub struct ReviewComment {
    pub id: u64,
    pub url: String,
    pub diff_hunk: String,
    pub path: String,
    pub position: u64,
    pub original_position: u64,
    pub commit_id: String,
    pub original_commit_id: String,
    pub user: User,
    pub body: String,
    pub created_at: String,
    pub updated_at: String,
    pub html_url: String,
    pub pull_request_url: String,
}
<commit_msg>mod doc<commit_after>//! Review comments interface

use super::{Github, Result};
use users::User;

/// A structure for interfacing with a issue comments
pub struct ReviewComments<'a> {
    github: &'a Github,
    owner: String,
    repo: String,
    number: u64,
}

impl<'a> ReviewComments<'a> {
    pub fn new<O, R>(github: &'a Github, owner: O, repo: R, number: u64) -> ReviewComments<'a>
        where O: Into<String>,
              R: Into<String>
    {
        ReviewComments {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
            number: number,
        }
    }

    /// list pull requests
    pub fn list(&self) -> Result<Vec<ReviewComment>> {
        self.github.get::<Vec<ReviewComment>>(&format!("/repos/{}/{}/pulls/{}/comments",
                                                       self.owner,
                                                       self.repo,
                                                       self.number))
    }
}

// representations

#[derive(Debug, Deserialize)]
pub struct ReviewComment {
    pub id: u64,
    pub url: String,
    pub diff_hunk: String,
    pub path: String,
    pub position: u64,
    pub original_position: u64,
    pub commit_id: String,
    pub original_commit_id: String,
    pub user: User,
    pub body: String,
    pub created_at: String,
    pub updated_at: String,
    pub html_url: String,
    pub pull_request_url: String,
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::iter::FromIterator;
use std::slice::Iter;
use std::vec;

use document::QName;
use document::dom4;

use super::EvaluationContext;

macro_rules! unpack(
    ($enum_name:ident, $name:ident, $wrapper:ident, dom4::$inner:ident) => (
        impl<'d> $enum_name<'d> {
            pub fn $name(self) -> Option<dom4::$inner<'d>> {
                match self {
                    $enum_name::$wrapper(n) => Some(n),
                    _ => None,
                }
            }
        }
    )
);

macro_rules! conversion_trait(
    ($tr_name:ident, $method:ident, $res_type:ident,
        { $(dom4::$leaf_type:ident => Node::$variant:ident),* }
    ) => (
        pub trait $tr_name<'d> {
            fn $method(self) -> $res_type<'d>;
        }

        impl<'d> $tr_name<'d> for $res_type<'d> {
            fn $method(self) -> $res_type<'d> {
                self
            }
        }

        $(impl<'d> $tr_name<'d> for dom4::$leaf_type<'d> {
            fn $method(self) -> $res_type<'d> {
                Node::$variant(self)
            }
        })*
    )
);

#[derive(Copy,Clone,PartialEq,Eq,Hash,Debug)]
pub enum Node<'d> {
    Root(dom4::Root<'d>),
    Element(dom4::Element<'d>),
    Attribute(dom4::Attribute<'d>),
    Text(dom4::Text<'d>),
    Comment(dom4::Comment<'d>),
    ProcessingInstruction(dom4::ProcessingInstruction<'d>),
}

unpack!(Node, root, Root, dom4::Root);
unpack!(Node, element, Element, dom4::Element);
unpack!(Node, attribute, Attribute, dom4::Attribute);
unpack!(Node, text, Text, dom4::Text);
unpack!(Node, comment, Comment, dom4::Comment);
unpack!(Node, processing_instruction, ProcessingInstruction, dom4::ProcessingInstruction);

impl<'d> Node<'d> {
    pub fn document(&self) -> &'d dom4::Document<'d> {
        use self::Node::*;
        match self {
            &Root(n)                  => n.document(),
            &Element(n)               => n.document(),
            &Attribute(n)             => n.document(),
            &Text(n)                  => n.document(),
            &Comment(n)               => n.document(),
            &ProcessingInstruction(n) => n.document(),
        }
    }

    pub fn expanded_name(&self) -> Option<QName<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => None,
            &Element(n)               => Some(n.name()),
            &Attribute(n)             => Some(n.name()),
            &Text(_)                  => None,
            &Comment(_)               => None,
            &ProcessingInstruction(n) => Some(QName::new(n.target())),
        }
    }

    pub fn parent(&self) -> Option<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => None,
            &Element(n)               => n.parent().map(|n| n.to_node()),
            &Attribute(n)             => n.parent().map(|n| n.to_node()),
            &Text(n)                  => n.parent().map(|n| n.to_node()),
            &Comment(n)               => n.parent().map(|n| n.to_node()),
            &ProcessingInstruction(n) => n.parent().map(|n| n.to_node()),
        }
    }

    pub fn children(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(n)                  => n.children().iter().map(|n| n.to_node()).collect(),
            &Element(n)               => n.children().iter().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(_)                  => Vec::new(),
            &Comment(_)               => Vec::new(),
            &ProcessingInstruction(_) => Vec::new(),
        }
    }

    pub fn preceding_siblings(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => Vec::new(),
            &Element(n)               => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(n)                  => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &Comment(n)               => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &ProcessingInstruction(n) => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
        }
    }

    pub fn following_siblings(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => Vec::new(),
            &Element(n)               => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(n)                  => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &Comment(n)               => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &ProcessingInstruction(n) => n.following_siblings().iter().map(|n| n.to_node()).collect(),
        }
    }
}

conversion_trait!(ToNode, to_node, Node, {
    dom4::Root => Node::Root,
    dom4::Element => Node::Element,
    dom4::Attribute => Node::Attribute,
    dom4::Text => Node::Text,
    dom4::Comment => Node::Comment,
    dom4::ProcessingInstruction => Node::ProcessingInstruction
});

impl<'d> ToNode<'d> for dom4::ChildOfRoot<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ChildOfRoot::Element(n)               => Element(n),
            dom4::ChildOfRoot::Comment(n)               => Comment(n),
            dom4::ChildOfRoot::ProcessingInstruction(n) => ProcessingInstruction(n),
        }
    }
}

impl<'d> ToNode<'d> for dom4::ChildOfElement<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ChildOfElement::Element(n)               => Element(n),
            dom4::ChildOfElement::Text(n)                  => Text(n),
            dom4::ChildOfElement::Comment(n)               => Comment(n),
            dom4::ChildOfElement::ProcessingInstruction(n) => ProcessingInstruction(n),
        }
    }
}

impl<'d> ToNode<'d> for dom4::ParentOfChild<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ParentOfChild::Root(n)    => Root(n),
            dom4::ParentOfChild::Element(n) => Element(n),
        }
    }
}

/// A collection of nodes
#[derive(PartialEq,Debug,Clone)]
pub struct Nodeset<'d> {
    nodes: Vec<Node<'d>>,
}

impl<'d> Nodeset<'d> {
    pub fn new() -> Nodeset<'d> {
        Nodeset { nodes: Vec::new() }
    }

    pub fn add<N : ToNode<'d>>(&mut self, node: N) {
        self.nodes.push(node.to_node());
    }

    pub fn iter(&self) -> Iter<Node<'d>> {
        self.nodes.iter()
    }

    pub fn add_nodeset(& mut self, other: &Nodeset<'d>) {
        self.nodes.push_all(&other.nodes);
    }

    pub fn size(&self) -> usize {
        self.nodes.len()
    }

    pub fn into_iter(self) -> vec::IntoIter<Node<'d>> {
        self.nodes.into_iter()
    }

    pub fn document_order_first(&self) -> Option<Node<'d>> {
        let doc = match self.nodes.first() {
            Some(n) => n.document(),
            None => return None,
        };

        let mut idx = 0;
        let mut stack = vec![doc.root().to_node()];
        let mut order: HashMap<Node, usize> = HashMap::new();

        // Rebuilding this each time cannot possibly be performant,
        // but I want to see how widely used this is first before
        // picking an appropriate caching point.

        while let Some(n) = stack.pop() {
            order.insert(n, idx);
            idx += 1;
            let c = n.children();

            stack.extend(c.into_iter().rev());

            if let Node::Element(e) = n {
                // TODO: namespaces
                stack.extend(e.attributes().into_iter().map(|a| a.to_node()));
            }
        }

        self.nodes.iter().min_by(|&&n| order[n]).map(|n| *n)
    }
}

impl<'a, 'd : 'a> FromIterator<EvaluationContext<'a, 'd>> for Nodeset<'d> {
    fn from_iter<T>(iterator: T) -> Nodeset<'d>
        where T: Iterator<Item=EvaluationContext<'a, 'd>>
    {
        let mut ns = Nodeset::new();
        for n in iterator { ns.add(n.node) };
        ns
    }
}

#[cfg(test)]
mod test {
    use document::Package;

    use super::Node::{
        Attribute,
        Comment,
        Element,
        ProcessingInstruction,
        Root,
        Text,
    };
    use super::{Nodeset,ToNode};

    #[test]
    fn nodeset_can_include_all_node_types() {
        let package = Package::new();
        let doc = package.as_document();
        let mut nodes = Nodeset::new();

        let r = doc.root();
        let e = doc.create_element("element");
        let a = e.set_attribute_value("name", "value");
        let t = doc.create_text("text");
        let c = doc.create_comment("comment");
        let p = doc.create_processing_instruction("pi", None);

        nodes.add(r);
        nodes.add(e);
        nodes.add(a);
        nodes.add(t);
        nodes.add(c);
        nodes.add(p);

        let node_vec: Vec<_> = nodes.iter().collect();

        assert_eq!(6, node_vec.len());
        assert_eq!(node_vec[0], &Root(r));
        assert_eq!(node_vec[1], &Element(e));
        assert_eq!(node_vec[2], &Attribute(a));
        assert_eq!(node_vec[3], &Text(t));
        assert_eq!(node_vec[4], &Comment(c));
        assert_eq!(node_vec[5], &ProcessingInstruction(p));
    }

    #[test]
    fn nodesets_can_be_combined() {
        let package = Package::new();
        let doc = package.as_document();

        let mut all_nodes = Nodeset::new();
        let mut nodes1 = Nodeset::new();
        let mut nodes2 = Nodeset::new();

        let e1 = doc.create_element("element1");
        let e2 = doc.create_element("element2");

        all_nodes.add(e1);
        all_nodes.add(e2);

        nodes1.add(e1);
        nodes2.add(e2);

        nodes1.add_nodeset(&nodes2);

        assert_eq!(all_nodes, nodes1);
    }

    #[test]
    fn nodeset_knows_first_node_in_document_order() {
        let package = Package::new();
        let doc = package.as_document();

        let c1 = doc.create_comment("1");
        let c2 = doc.create_comment("2");
        doc.root().append_child(c1);
        doc.root().append_child(c2);

        let nodes = nodeset![c2, c1];

        assert_eq!(Some(c1.to_node()), nodes.document_order_first());
    }

    #[test]
    fn attributes_come_before_children_in_document_order() {
        let package = Package::new();
        let doc = package.as_document();

        let parent = doc.create_element("parent");
        let attr = parent.set_attribute_value("a", "v");
        let child = doc.create_element("child");

        doc.root().append_child(parent);
        parent.append_child(child);

        let nodes = nodeset![child, attr];

        assert_eq!(Some(attr.to_node()), nodes.document_order_first());
    }
}
<commit_msg>Node learned the concept of a prefixed name<commit_after>use std::collections::HashMap;
use std::iter::FromIterator;
use std::slice::Iter;
use std::vec;

use document::QName;
use document::dom4;

use super::EvaluationContext;

macro_rules! unpack(
    ($enum_name:ident, $name:ident, $wrapper:ident, dom4::$inner:ident) => (
        impl<'d> $enum_name<'d> {
            pub fn $name(self) -> Option<dom4::$inner<'d>> {
                match self {
                    $enum_name::$wrapper(n) => Some(n),
                    _ => None,
                }
            }
        }
    )
);

macro_rules! conversion_trait(
    ($tr_name:ident, $method:ident, $res_type:ident,
        { $(dom4::$leaf_type:ident => Node::$variant:ident),* }
    ) => (
        pub trait $tr_name<'d> {
            fn $method(self) -> $res_type<'d>;
        }

        impl<'d> $tr_name<'d> for $res_type<'d> {
            fn $method(self) -> $res_type<'d> {
                self
            }
        }

        $(impl<'d> $tr_name<'d> for dom4::$leaf_type<'d> {
            fn $method(self) -> $res_type<'d> {
                Node::$variant(self)
            }
        })*
    )
);

#[derive(Copy,Clone,PartialEq,Eq,Hash,Debug)]
pub enum Node<'d> {
    Root(dom4::Root<'d>),
    Element(dom4::Element<'d>),
    Attribute(dom4::Attribute<'d>),
    Text(dom4::Text<'d>),
    Comment(dom4::Comment<'d>),
    ProcessingInstruction(dom4::ProcessingInstruction<'d>),
}

unpack!(Node, root, Root, dom4::Root);
unpack!(Node, element, Element, dom4::Element);
unpack!(Node, attribute, Attribute, dom4::Attribute);
unpack!(Node, text, Text, dom4::Text);
unpack!(Node, comment, Comment, dom4::Comment);
unpack!(Node, processing_instruction, ProcessingInstruction, dom4::ProcessingInstruction);

impl<'d> Node<'d> {
    pub fn document(&self) -> &'d dom4::Document<'d> {
        use self::Node::*;
        match self {
            &Root(n)                  => n.document(),
            &Element(n)               => n.document(),
            &Attribute(n)             => n.document(),
            &Text(n)                  => n.document(),
            &Comment(n)               => n.document(),
            &ProcessingInstruction(n) => n.document(),
        }
    }

    pub fn prefixed_name(&self) -> Option<String> {
        use self::Node::*;

        fn ensure_prefixed_name(element: dom4::Element, name: QName, preferred_prefix: Option<&str>) -> String {
            if let Some(ns_uri) = name.namespace_uri() {
                let prefix = element
                    .prefix_for_namespace_uri(ns_uri, preferred_prefix)
                    .unwrap_or_else(|| element.generate_prefix(ns_uri, preferred_prefix));
                format!("{}:{}", prefix, name.local_part())
            } else {
                name.local_part().to_string()
            }
        };

        match self {
            &Root(_)                  => None,
            &Element(n)               => {
                Some(ensure_prefixed_name(n, n.name(), n.preferred_prefix()))
            },
            &Attribute(n)             => {
                let parent = n.parent().expect("Cannot process attribute without parent");
                Some(ensure_prefixed_name(parent, n.name(), n.preferred_prefix()))
            },
            &Text(_)                  => None,
            &Comment(_)               => None,
            &ProcessingInstruction(n) => Some(n.target().to_string()),
        }
    }

    pub fn expanded_name(&self) -> Option<QName<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => None,
            &Element(n)               => Some(n.name()),
            &Attribute(n)             => Some(n.name()),
            &Text(_)                  => None,
            &Comment(_)               => None,
            &ProcessingInstruction(n) => Some(QName::new(n.target())),
        }
    }

    pub fn parent(&self) -> Option<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => None,
            &Element(n)               => n.parent().map(|n| n.to_node()),
            &Attribute(n)             => n.parent().map(|n| n.to_node()),
            &Text(n)                  => n.parent().map(|n| n.to_node()),
            &Comment(n)               => n.parent().map(|n| n.to_node()),
            &ProcessingInstruction(n) => n.parent().map(|n| n.to_node()),
        }
    }

    pub fn children(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(n)                  => n.children().iter().map(|n| n.to_node()).collect(),
            &Element(n)               => n.children().iter().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(_)                  => Vec::new(),
            &Comment(_)               => Vec::new(),
            &ProcessingInstruction(_) => Vec::new(),
        }
    }

    pub fn preceding_siblings(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => Vec::new(),
            &Element(n)               => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(n)                  => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &Comment(n)               => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &ProcessingInstruction(n) => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
        }
    }

    pub fn following_siblings(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => Vec::new(),
            &Element(n)               => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(n)                  => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &Comment(n)               => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &ProcessingInstruction(n) => n.following_siblings().iter().map(|n| n.to_node()).collect(),
        }
    }
}

conversion_trait!(ToNode, to_node, Node, {
    dom4::Root => Node::Root,
    dom4::Element => Node::Element,
    dom4::Attribute => Node::Attribute,
    dom4::Text => Node::Text,
    dom4::Comment => Node::Comment,
    dom4::ProcessingInstruction => Node::ProcessingInstruction
});

impl<'d> ToNode<'d> for dom4::ChildOfRoot<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ChildOfRoot::Element(n)               => Element(n),
            dom4::ChildOfRoot::Comment(n)               => Comment(n),
            dom4::ChildOfRoot::ProcessingInstruction(n) => ProcessingInstruction(n),
        }
    }
}

impl<'d> ToNode<'d> for dom4::ChildOfElement<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ChildOfElement::Element(n)               => Element(n),
            dom4::ChildOfElement::Text(n)                  => Text(n),
            dom4::ChildOfElement::Comment(n)               => Comment(n),
            dom4::ChildOfElement::ProcessingInstruction(n) => ProcessingInstruction(n),
        }
    }
}

impl<'d> ToNode<'d> for dom4::ParentOfChild<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ParentOfChild::Root(n)    => Root(n),
            dom4::ParentOfChild::Element(n) => Element(n),
        }
    }
}

/// A collection of nodes
#[derive(PartialEq,Debug,Clone)]
pub struct Nodeset<'d> {
    nodes: Vec<Node<'d>>,
}

impl<'d> Nodeset<'d> {
    pub fn new() -> Nodeset<'d> {
        Nodeset { nodes: Vec::new() }
    }

    pub fn add<N : ToNode<'d>>(&mut self, node: N) {
        self.nodes.push(node.to_node());
    }

    pub fn iter(&self) -> Iter<Node<'d>> {
        self.nodes.iter()
    }

    pub fn add_nodeset(& mut self, other: &Nodeset<'d>) {
        self.nodes.push_all(&other.nodes);
    }

    pub fn size(&self) -> usize {
        self.nodes.len()
    }

    pub fn into_iter(self) -> vec::IntoIter<Node<'d>> {
        self.nodes.into_iter()
    }

    pub fn document_order_first(&self) -> Option<Node<'d>> {
        let doc = match self.nodes.first() {
            Some(n) => n.document(),
            None => return None,
        };

        let mut idx = 0;
        let mut stack = vec![doc.root().to_node()];
        let mut order: HashMap<Node, usize> = HashMap::new();

        // Rebuilding this each time cannot possibly be performant,
        // but I want to see how widely used this is first before
        // picking an appropriate caching point.

        while let Some(n) = stack.pop() {
            order.insert(n, idx);
            idx += 1;
            let c = n.children();

            stack.extend(c.into_iter().rev());

            if let Node::Element(e) = n {
                // TODO: namespaces
                stack.extend(e.attributes().into_iter().map(|a| a.to_node()));
            }
        }

        self.nodes.iter().min_by(|&&n| order[n]).map(|n| *n)
    }
}

impl<'a, 'd : 'a> FromIterator<EvaluationContext<'a, 'd>> for Nodeset<'d> {
    fn from_iter<T>(iterator: T) -> Nodeset<'d>
        where T: Iterator<Item=EvaluationContext<'a, 'd>>
    {
        let mut ns = Nodeset::new();
        for n in iterator { ns.add(n.node) };
        ns
    }
}

#[cfg(test)]
mod test {
    use document::Package;

    use super::Node::{
        Attribute,
        Comment,
        Element,
        ProcessingInstruction,
        Root,
        Text,
    };
    use super::{Nodeset,ToNode};

    #[test]
    fn nodeset_can_include_all_node_types() {
        let package = Package::new();
        let doc = package.as_document();
        let mut nodes = Nodeset::new();

        let r = doc.root();
        let e = doc.create_element("element");
        let a = e.set_attribute_value("name", "value");
        let t = doc.create_text("text");
        let c = doc.create_comment("comment");
        let p = doc.create_processing_instruction("pi", None);

        nodes.add(r);
        nodes.add(e);
        nodes.add(a);
        nodes.add(t);
        nodes.add(c);
        nodes.add(p);

        let node_vec: Vec<_> = nodes.iter().collect();

        assert_eq!(6, node_vec.len());
        assert_eq!(node_vec[0], &Root(r));
        assert_eq!(node_vec[1], &Element(e));
        assert_eq!(node_vec[2], &Attribute(a));
        assert_eq!(node_vec[3], &Text(t));
        assert_eq!(node_vec[4], &Comment(c));
        assert_eq!(node_vec[5], &ProcessingInstruction(p));
    }

    #[test]
    fn nodesets_can_be_combined() {
        let package = Package::new();
        let doc = package.as_document();

        let mut all_nodes = Nodeset::new();
        let mut nodes1 = Nodeset::new();
        let mut nodes2 = Nodeset::new();

        let e1 = doc.create_element("element1");
        let e2 = doc.create_element("element2");

        all_nodes.add(e1);
        all_nodes.add(e2);

        nodes1.add(e1);
        nodes2.add(e2);

        nodes1.add_nodeset(&nodes2);

        assert_eq!(all_nodes, nodes1);
    }

    #[test]
    fn nodeset_knows_first_node_in_document_order() {
        let package = Package::new();
        let doc = package.as_document();

        let c1 = doc.create_comment("1");
        let c2 = doc.create_comment("2");
        doc.root().append_child(c1);
        doc.root().append_child(c2);

        let nodes = nodeset![c2, c1];

        assert_eq!(Some(c1.to_node()), nodes.document_order_first());
    }

    #[test]
    fn attributes_come_before_children_in_document_order() {
        let package = Package::new();
        let doc = package.as_document();

        let parent = doc.create_element("parent");
        let attr = parent.set_attribute_value("a", "v");
        let child = doc.create_element("child");

        doc.root().append_child(parent);
        parent.append_child(child);

        let nodes = nodeset![child, attr];

        assert_eq!(Some(attr.to_node()), nodes.document_order_first());
    }

    #[test]
    fn prefixed_name_of_element_with_preferred_prefix() {
        let package = Package::new();
        let doc = package.as_document();

        let e = doc.create_element(("uri", "wow"));
        e.set_preferred_prefix(Some("prefix"));
        let node = e.to_node();

        assert_eq!(Some("prefix:wow".to_string()), node.prefixed_name());
    }

    #[test]
    fn prefixed_name_of_element_with_auto_prefix() {
        let package = Package::new();
        let doc = package.as_document();

        let e = doc.create_element(("uri", "wow"));
        let node = e.to_node();

        assert_eq!(Some("autons0:wow".to_string()), node.prefixed_name());
    }

    #[test]
    fn prefixed_name_of_attribute_with_preferred_prefix() {
        let package = Package::new();
        let doc = package.as_document();

        let e = doc.create_element("element");
        let a = e.set_attribute_value(("uri", "attr"), "value");
        a.set_preferred_prefix(Some("prefix"));
        let node = a.to_node();

        assert_eq!(Some("prefix:attr".to_string()), node.prefixed_name());
    }

    #[test]
    fn prefixed_name_of_attribute_with_auto_prefix() {
        let package = Package::new();
        let doc = package.as_document();

        let e = doc.create_element("element");
        let a = e.set_attribute_value(("uri", "attr"), "value");
        let node = a.to_node();

        assert_eq!(Some("autons0:attr".to_string()), node.prefixed_name());
    }

    #[test]
    fn prefixed_name_of_processing_instruction() {
        let package = Package::new();
        let doc = package.as_document();

        let pi = doc.create_processing_instruction("target", Some("value"));
        let node = pi.to_node();

        assert_eq!(Some("target".to_string()), node.prefixed_name());
    }
}
<|endoftext|>


<commit_before>use std::ops::Mul;

use ::{FromPrimitive, Num, ToPrimitive};
use ::{DenseMatrix, IdentityMatrix, Matrix, SparseMatrix, ZeroMatrix};

static MUL_DIM_ERROR: &str = "Cannot multiply matrices of given dimensions";

macro_rules! check_mul_dims {
    ($self:expr, $rhs:expr) => (
        if $self.cols() != $rhs.rows() {
            panic!("{}: lhs={} rhs={}", MUL_DIM_ERROR, $self, $rhs)
        }
    )
}

macro_rules! zero_mul_impl {
    ($($t:ty)*) => ($(
        impl<T: Clone + Num + FromPrimitive + ToPrimitive>
            Mul<$t> for ZeroMatrix<T>
            where T: Copy,
        {
            type Output = ZeroMatrix<T>;

            #[inline]
            fn mul(self, rhs: $t) -> ZeroMatrix<T> {
                check_mul_dims!(self, rhs);
                ZeroMatrix::new(self.rows(), rhs.cols())
            }
        }

        impl<T: Clone + Num + FromPrimitive + ToPrimitive>
            Mul<ZeroMatrix<T>> for $t
            where T: Copy,
        {
            type Output = ZeroMatrix<T>;

            #[inline]
            fn mul(self, rhs: ZeroMatrix<T>) -> ZeroMatrix<T> {
                check_mul_dims!(self, rhs);
                ZeroMatrix::new(self.rows(), rhs.cols())
            }
        }
    )*)
}

impl<T: Clone + Num + FromPrimitive + ToPrimitive> Mul for ZeroMatrix<T>
    where T: Copy,
{
    type Output = ZeroMatrix<T>;

    #[inline]
    fn mul(self, rhs: ZeroMatrix<T>) -> ZeroMatrix<T> {
        check_mul_dims!(self, rhs);
        ZeroMatrix::new(self.rows(), rhs.cols())
    }
}

zero_mul_impl! { DenseMatrix<T> IdentityMatrix<T> SparseMatrix<T> }

macro_rules! ident_mul_impl {
    ($($t:ty)*) => ($(
        impl<T: Clone + Num + FromPrimitive + ToPrimitive>
            Mul<$t> for IdentityMatrix<T>
            where T: Copy,
        {
            type Output = $t;

            #[inline]
            fn mul(self, rhs: $t) -> $t {
                check_mul_dims!(self, rhs);
                rhs
            }
        }

        impl<T: Clone + Num + FromPrimitive + ToPrimitive>
            Mul<IdentityMatrix<T>> for $t
            where T: Copy,
        {
            type Output = $t;

            #[inline]
            fn mul(self, rhs: IdentityMatrix<T>) -> $t {
                check_mul_dims!(self, rhs);
                self
            }
        }
    )*)
}

impl<T: Clone + Num + FromPrimitive + ToPrimitive>
    Mul for IdentityMatrix<T>
    where T: Copy,
{
    type Output = IdentityMatrix<T>;

    #[inline]
    fn mul(self, _rhs: IdentityMatrix<T>) -> IdentityMatrix<T> {
        self
    }
}

ident_mul_impl! { DenseMatrix<T> SparseMatrix<T> }

#[cfg(test)]
mod tests {
    #![allow(non_snake_case)]
    use ::{IdentityMatrix, Matrix, ZeroMatrix};

    #[test]
    fn test_good_zero_zero_mul() {
        let Z1: ZeroMatrix<usize> = zeros!(69, 21);
        let Z2 = zeros!(21, 42);
        let Z3 = zeros!(69, 42);
        assert_eq!(Z1.clone()*Z2.clone(), Z3);

        let Z4 = Z1.transpose();
        let Z5 = Z3.clone();
        let Z6 = zeros!(21, 42);
        assert_eq!(Z4*Z5, Z6);
    }

    #[test]
    #[should_panic]
    fn test_bad_zero_zero_mul() {
        let Z1: ZeroMatrix<usize> = zeros!(21, 69);
        let Z2 = zeros!(21, 42);
        let _panic = Z1*Z2;
    }

    #[test]
    fn test_ident_mul() {
        let I: IdentityMatrix<usize> = eye!(5);
        let S = sparse!(5, 5);
        for i in 0..5 {
            for j in 0..5 {
                S.set(i, j, 5*i+j);
            }
        }
        let D = dense![0, 1, 2, 3, 4;
                       5, 6, 7, 8, 9;
                      10,11,12,13,14;
                      15,16,17,18,19;
                      20,21,22,23,24].unwrap();
        assert_eq!(I.clone()*S.clone(), S.clone());
        assert_eq!(S.clone()*I.clone(), S);
        assert_eq!(I.clone()*D.clone(), D.clone());
        assert_eq!(D.clone()*I, D);
    }
}
<commit_msg>Move zero and ident mul implementation into ident_mul_impl macro since it does not allocate new memory<commit_after>use std::ops::Mul;

use ::{FromPrimitive, Num, ToPrimitive};
use ::{DenseMatrix, IdentityMatrix, Matrix, SparseMatrix, ZeroMatrix};

static MUL_DIM_ERROR: &str = "Cannot multiply matrices of given dimensions";

macro_rules! check_mul_dims {
    ($self:expr, $rhs:expr) => (
        if $self.cols() != $rhs.rows() {
            panic!("{}: lhs={} rhs={}", MUL_DIM_ERROR, $self, $rhs)
        }
    )
}

macro_rules! zero_mul_impl {
    ($($t:ty)*) => ($(
        impl<T: Clone + Num + FromPrimitive + ToPrimitive>
            Mul<$t> for ZeroMatrix<T>
            where T: Copy,
        {
            type Output = ZeroMatrix<T>;

            #[inline]
            fn mul(self, rhs: $t) -> ZeroMatrix<T> {
                check_mul_dims!(self, rhs);
                ZeroMatrix::new(self.rows(), rhs.cols())
            }
        }

        impl<T: Clone + Num + FromPrimitive + ToPrimitive>
            Mul<ZeroMatrix<T>> for $t
            where T: Copy,
        {
            type Output = ZeroMatrix<T>;

            #[inline]
            fn mul(self, rhs: ZeroMatrix<T>) -> ZeroMatrix<T> {
                check_mul_dims!(self, rhs);
                ZeroMatrix::new(self.rows(), rhs.cols())
            }
        }
    )*)
}

impl<T: Clone + Num + FromPrimitive + ToPrimitive> Mul for ZeroMatrix<T>
    where T: Copy,
{
    type Output = ZeroMatrix<T>;

    #[inline]
    fn mul(self, rhs: ZeroMatrix<T>) -> ZeroMatrix<T> {
        check_mul_dims!(self, rhs);
        ZeroMatrix::new(self.rows(), rhs.cols())
    }
}

zero_mul_impl! { DenseMatrix<T> SparseMatrix<T> }

macro_rules! ident_mul_impl {
    ($($t:ty)*) => ($(
        impl<T: Clone + Num + FromPrimitive + ToPrimitive>
            Mul<$t> for IdentityMatrix<T>
            where T: Copy,
        {
            type Output = $t;

            #[inline]
            fn mul(self, rhs: $t) -> $t {
                check_mul_dims!(self, rhs);
                rhs
            }
        }

        impl<T: Clone + Num + FromPrimitive + ToPrimitive>
            Mul<IdentityMatrix<T>> for $t
            where T: Copy,
        {
            type Output = $t;

            #[inline]
            fn mul(self, rhs: IdentityMatrix<T>) -> $t {
                check_mul_dims!(self, rhs);
                self
            }
        }
    )*)
}

impl<T: Clone + Num + FromPrimitive + ToPrimitive>
    Mul for IdentityMatrix<T>
    where T: Copy,
{
    type Output = IdentityMatrix<T>;

    #[inline]
    fn mul(self, _rhs: IdentityMatrix<T>) -> IdentityMatrix<T> {
        self
    }
}

ident_mul_impl! { DenseMatrix<T> SparseMatrix<T> ZeroMatrix<T> }

#[cfg(test)]
mod tests {
    #![allow(non_snake_case)]
    use ::{IdentityMatrix, Matrix, ZeroMatrix};

    #[test]
    fn test_good_zero_zero_mul() {
        let Z1: ZeroMatrix<usize> = zeros!(69, 21);
        let Z2 = zeros!(21, 42);
        let Z3 = zeros!(69, 42);
        assert_eq!(Z1.clone()*Z2.clone(), Z3);

        let Z4 = Z1.transpose();
        let Z5 = Z3.clone();
        let Z6 = zeros!(21, 42);
        assert_eq!(Z4*Z5, Z6);
    }

    #[test]
    #[should_panic]
    fn test_bad_zero_zero_mul() {
        let Z1: ZeroMatrix<usize> = zeros!(21, 69);
        let Z2 = zeros!(21, 42);
        let _panic = Z1*Z2;
    }

    #[test]
    fn test_ident_mul() {
        let I: IdentityMatrix<usize> = eye!(5);
        let S = sparse!(5, 5);
        for i in 0..5 {
            for j in 0..5 {
                S.set(i, j, 5*i+j);
            }
        }
        let D = dense![0, 1, 2, 3, 4;
                       5, 6, 7, 8, 9;
                      10,11,12,13,14;
                      15,16,17,18,19;
                      20,21,22,23,24].unwrap();
        assert_eq!(I.clone()*S.clone(), S.clone());
        assert_eq!(S.clone()*I.clone(), S);
        assert_eq!(I.clone()*D.clone(), D.clone());
        assert_eq!(D.clone()*I, D);
    }
}
<|endoftext|>


<commit_before>use std::ffi::CString;
use std::ptr;

use class::TypeParams;
use ctxt::{Fct, FctId, FctParent, FctSrc, SemContext};
use ty::{BuiltinType, MachineMode};

use dora_parser::ast::*;
use dora_parser::ast::Expr::*;
use dora_parser::ast::Stmt::*;

use llvm;
use llvm::core::*;
// use llvm::execution_engine::*;
// use llvm::target::*;

pub fn generate<'ast>(
    ctxt: &SemContext<'ast>,
    id: FctId,
    cls_type_params: &TypeParams,
    fct_type_params: &TypeParams,
) -> Result<*const u8, ()> {
    let fct = ctxt.fcts[id].borrow();
    let src = fct.src();
    let mut src = src.borrow_mut();

    generate_fct(ctxt, &fct, &mut src, cls_type_params, fct_type_params)
}

pub fn generate_fct<'ast>(
    ctxt: &SemContext<'ast>,
    fct: &Fct<'ast>,
    src: &mut FctSrc,
    cls_type_params: &TypeParams,
    fct_type_params: &TypeParams,
) -> Result<*const u8, ()> {
    let name = fct.full_name(ctxt);

    let mut cg = CodeGen {
        ctxt: ctxt,
        fct: fct,
        ast: fct.ast,
        src: src,
        cls_type_params,
        fct_type_params,

        fct_name: CString::new(name).unwrap(),

        context: ptr::null_mut(),
        module: ptr::null_mut(),
        builder: ptr::null_mut(),
        function: ptr::null_mut(),
    };

    cg.generate()
}

type EmitResult<T> = Result<T, ()>;

fn fail<T>() -> EmitResult<T> {
    Err(())
}

fn ok<T>(value: T) -> EmitResult<T> {
    Ok(value)
}

struct CodeGen<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    fct: &'a Fct<'ast>,
    ast: &'ast Function,
    src: &'a mut FctSrc,
    cls_type_params: &'a TypeParams,
    fct_type_params: &'a TypeParams,

    fct_name: CString,

    context: *mut llvm::LLVMContext,
    module: *mut llvm::LLVMModule,
    builder: *mut llvm::LLVMBuilder,
    function: *mut llvm::LLVMValue,
}

impl<'a, 'ast> CodeGen<'a, 'ast>
where
    'ast: 'a,
{
    fn generate(&mut self) -> Result<*const u8, ()> {
        self.init();
        self.create_function()?;

        let block = self.ast.block.as_ref().unwrap();
        self.emit_stmt(block)?;

        Err(())
    }

    fn init(&mut self) {
        unsafe {
            self.context = LLVMContextCreate();
            self.module = LLVMModuleCreateWithNameInContext(
                self.fct_name.as_ptr(),
                self.context,
            );
            self.builder = LLVMCreateBuilderInContext(self.context);
        }
    }

    fn create_function(&mut self) -> Result<(), ()> {
        let mut params = Vec::with_capacity(self.ast.params.len());

        for param in &self.ast.params {
            let ty = self.ty(param.id);
            params.push(self.llvm_ty(ty));
        }

        let return_type = self.specialize_type(self.fct.return_type);
        let llvm_return_type = self.llvm_ty(return_type);

        unsafe {
            let function_type = LLVMFunctionType(
                llvm_return_type,
                params.as_mut_ptr(),
                params.len() as u32,
                0);

            self.function = LLVMAddFunction(self.module, self.fct_name.as_ptr(), function_type);
        }

        Err(())
    }

    fn emit_stmt(&mut self, s: &'ast Stmt) -> EmitResult<()> {
        match *s {
            StmtExpr(ref stmt) => {
                self.emit_expr(&stmt.expr)?;

                Ok(())
            }

            StmtIf(_) => fail(),
            StmtLoop(_) => fail(),
            StmtWhile(_) => fail(),
            StmtFor(_) => fail(),
            StmtReturn(ref stmt) => self.emit_return(stmt),
            StmtBreak(_) => fail(),
            StmtContinue(_) => fail(),
            StmtBlock(_) => fail(),
            StmtVar(_) => fail(),
            StmtThrow(_) => fail(),
            StmtDefer(_) => fail(),
            StmtDo(_) => fail(),
            StmtSpawn(_) => fail(),
        }
    }

    fn emit_expr(&mut self, e: &'ast Expr) -> EmitResult<*mut llvm::LLVMValue> {
        match *e {
            ExprLitChar(_) => fail(),
            ExprLitInt(_) => fail(),
            ExprLitFloat(_) => fail(),
            ExprLitBool(_) => fail(),
            ExprLitStr(_) => fail(),
            ExprLitStruct(_) => fail(),
            ExprUn(_) => fail(),
            ExprIdent(_) => fail(),
            ExprAssign(_) => fail(),
            ExprBin(_) => fail(),
            ExprCall(_) => fail(),
            ExprDelegation(_) => fail(),
            ExprField(_) => fail(),
            ExprSelf(_) => fail(),
            ExprSuper(_) => fail(),
            ExprNil(_) => fail(),
            ExprArray(_) => fail(),
            ExprConv(_) => fail(),
            ExprTry(_) => fail(),
            ExprLambda(_) => fail(),
        }
    }

    fn emit_return(&mut self, s: &'ast StmtReturnType) -> EmitResult<()> {
        if let Some(ref expr) = s.expr {
            let value = self.emit_expr(expr)?;

            unsafe {
                LLVMBuildRet(self.builder, value);
            }

        } else {
            unsafe {
                LLVMBuildRetVoid(self.builder);
            }
        }

        ok(())
    }

    fn ty(&self, id: NodeId) -> BuiltinType {
        let ty = self.src.ty(id);
        self.specialize_type(ty)
    }

    fn specialize_type(&self, ty: BuiltinType) -> BuiltinType {
        match ty {
            BuiltinType::ClassTypeParam(cls_id, id) => {
                assert!(self.fct.parent == FctParent::Class(cls_id));
                self.cls_type_params[id.idx()]
            }

            BuiltinType::FctTypeParam(fct_id, id) => {
                assert!(self.fct.id == fct_id);
                self.fct_type_params[id.idx()]
            }

            BuiltinType::Class(cls_id, list_id) => {
                let params = self.ctxt.lists.borrow().get(list_id);

                let params: Vec<_> = params.iter().map(|t| self.specialize_type(t)).collect();

                let list_id = self.ctxt.lists.borrow_mut().insert(params.into());

                BuiltinType::Class(cls_id, list_id)
            }

            BuiltinType::Lambda(_) => unimplemented!(),

            _ => ty,
        }
    }

    fn llvm_ty(&self, ty: BuiltinType) -> *mut llvm::LLVMType {
        let mode = ty.mode();

        unsafe {
            match mode {
                MachineMode::Int64 => LLVMInt64TypeInContext(self.context),
                MachineMode::Int32 => LLVMInt32TypeInContext(self.context),
                MachineMode::Int8 => LLVMInt8TypeInContext(self.context),
                MachineMode::Float32 => LLVMFloatTypeInContext(self.context),
                MachineMode::Float64 => LLVMDoubleTypeInContext(self.context),
                MachineMode::Ptr => {
                    let int8 = LLVMInt8TypeInContext(self.context);
                    LLVMPointerType(int8, 1)
                }
            }
        }
    }
}
<commit_msg>opt: add entry block<commit_after>use std::ffi::CString;
use std::ptr;

use class::TypeParams;
use ctxt::{Fct, FctId, FctParent, FctSrc, SemContext};
use ty::{BuiltinType, MachineMode};

use dora_parser::ast::*;
use dora_parser::ast::Expr::*;
use dora_parser::ast::Stmt::*;

use llvm;
use llvm::core::*;
// use llvm::execution_engine::*;
// use llvm::target::*;

pub fn generate<'ast>(
    ctxt: &SemContext<'ast>,
    id: FctId,
    cls_type_params: &TypeParams,
    fct_type_params: &TypeParams,
) -> Result<*const u8, ()> {
    let fct = ctxt.fcts[id].borrow();
    let src = fct.src();
    let mut src = src.borrow_mut();

    generate_fct(ctxt, &fct, &mut src, cls_type_params, fct_type_params)
}

pub fn generate_fct<'ast>(
    ctxt: &SemContext<'ast>,
    fct: &Fct<'ast>,
    src: &mut FctSrc,
    cls_type_params: &TypeParams,
    fct_type_params: &TypeParams,
) -> Result<*const u8, ()> {
    let name = fct.full_name(ctxt);

    let mut cg = CodeGen {
        ctxt: ctxt,
        fct: fct,
        ast: fct.ast,
        src: src,
        cls_type_params,
        fct_type_params,

        fct_name: CString::new(name).unwrap(),

        context: ptr::null_mut(),
        module: ptr::null_mut(),
        builder: ptr::null_mut(),
        function: ptr::null_mut(),
    };

    cg.generate()
}

type EmitResult<T> = Result<T, ()>;

fn fail<T>() -> EmitResult<T> {
    Err(())
}

fn ok<T>(value: T) -> EmitResult<T> {
    Ok(value)
}

struct CodeGen<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    fct: &'a Fct<'ast>,
    ast: &'ast Function,
    src: &'a mut FctSrc,
    cls_type_params: &'a TypeParams,
    fct_type_params: &'a TypeParams,

    fct_name: CString,

    context: *mut llvm::LLVMContext,
    module: *mut llvm::LLVMModule,
    builder: *mut llvm::LLVMBuilder,
    function: *mut llvm::LLVMValue,
}

impl<'a, 'ast> CodeGen<'a, 'ast>
where
    'ast: 'a,
{
    fn generate(&mut self) -> EmitResult<*const u8> {
        self.init();
        self.create_function()?;
        self.add_entry_bb();

        let block = self.ast.block.as_ref().unwrap();
        self.emit_stmt(block)?;

        fail()
    }

    fn init(&mut self) {
        unsafe {
            self.context = LLVMContextCreate();
            self.module = LLVMModuleCreateWithNameInContext(
                self.fct_name.as_ptr(),
                self.context,
            );
            self.builder = LLVMCreateBuilderInContext(self.context);
        }
    }

    fn create_function(&mut self) -> EmitResult<()> {
        let mut params = Vec::with_capacity(self.ast.params.len());

        for param in &self.ast.params {
            let ty = self.ty(param.id);
            params.push(self.llvm_ty(ty));
        }

        let return_type = self.specialize_type(self.fct.return_type);
        let llvm_return_type = self.llvm_ty(return_type);

        unsafe {
            let function_type = LLVMFunctionType(
                llvm_return_type,
                params.as_mut_ptr(),
                params.len() as u32,
                0);

            self.function = LLVMAddFunction(self.module, self.fct_name.as_ptr(), function_type);
        }

        ok(())
    }

    fn add_entry_bb(&mut self) {
        unsafe {
            let bb = LLVMAppendBasicBlockInContext(
                self.context,
                self.function,
                b"entry\0".as_ptr() as *const _);

            LLVMPositionBuilderAtEnd(self.builder, bb);
        }
    }

    fn emit_stmt(&mut self, s: &'ast Stmt) -> EmitResult<()> {
        match *s {
            StmtExpr(ref stmt) => {
                self.emit_expr(&stmt.expr)?;

                Ok(())
            }

            StmtIf(_) => fail(),
            StmtLoop(_) => fail(),
            StmtWhile(_) => fail(),
            StmtFor(_) => fail(),
            StmtReturn(ref stmt) => self.emit_return(stmt),
            StmtBreak(_) => fail(),
            StmtContinue(_) => fail(),
            StmtBlock(_) => fail(),
            StmtVar(_) => fail(),
            StmtThrow(_) => fail(),
            StmtDefer(_) => fail(),
            StmtDo(_) => fail(),
            StmtSpawn(_) => fail(),
        }
    }

    fn emit_expr(&mut self, e: &'ast Expr) -> EmitResult<*mut llvm::LLVMValue> {
        match *e {
            ExprLitChar(_) => fail(),
            ExprLitInt(_) => fail(),
            ExprLitFloat(_) => fail(),
            ExprLitBool(_) => fail(),
            ExprLitStr(_) => fail(),
            ExprLitStruct(_) => fail(),
            ExprUn(_) => fail(),
            ExprIdent(_) => fail(),
            ExprAssign(_) => fail(),
            ExprBin(_) => fail(),
            ExprCall(_) => fail(),
            ExprDelegation(_) => fail(),
            ExprField(_) => fail(),
            ExprSelf(_) => fail(),
            ExprSuper(_) => fail(),
            ExprNil(_) => fail(),
            ExprArray(_) => fail(),
            ExprConv(_) => fail(),
            ExprTry(_) => fail(),
            ExprLambda(_) => fail(),
        }
    }

    fn emit_return(&mut self, s: &'ast StmtReturnType) -> EmitResult<()> {
        if let Some(ref expr) = s.expr {
            let value = self.emit_expr(expr)?;

            unsafe {
                LLVMBuildRet(self.builder, value);
            }

        } else {
            unsafe {
                LLVMBuildRetVoid(self.builder);
            }
        }

        ok(())
    }

    fn ty(&self, id: NodeId) -> BuiltinType {
        let ty = self.src.ty(id);
        self.specialize_type(ty)
    }

    fn specialize_type(&self, ty: BuiltinType) -> BuiltinType {
        match ty {
            BuiltinType::ClassTypeParam(cls_id, id) => {
                assert!(self.fct.parent == FctParent::Class(cls_id));
                self.cls_type_params[id.idx()]
            }

            BuiltinType::FctTypeParam(fct_id, id) => {
                assert!(self.fct.id == fct_id);
                self.fct_type_params[id.idx()]
            }

            BuiltinType::Class(cls_id, list_id) => {
                let params = self.ctxt.lists.borrow().get(list_id);

                let params: Vec<_> = params.iter().map(|t| self.specialize_type(t)).collect();

                let list_id = self.ctxt.lists.borrow_mut().insert(params.into());

                BuiltinType::Class(cls_id, list_id)
            }

            BuiltinType::Lambda(_) => unimplemented!(),

            _ => ty,
        }
    }

    fn llvm_ty(&self, ty: BuiltinType) -> *mut llvm::LLVMType {
        let mode = ty.mode();

        unsafe {
            match mode {
                MachineMode::Int64 => LLVMInt64TypeInContext(self.context),
                MachineMode::Int32 => LLVMInt32TypeInContext(self.context),
                MachineMode::Int8 => LLVMInt8TypeInContext(self.context),
                MachineMode::Float32 => LLVMFloatTypeInContext(self.context),
                MachineMode::Float64 => LLVMDoubleTypeInContext(self.context),
                MachineMode::Ptr => {
                    let int8 = LLVMInt8TypeInContext(self.context);
                    LLVMPointerType(int8, 1)
                }
            }
        }
    }
}
<|endoftext|>