Skip to content

shootout-meteor improvement #14063

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 10, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 111 additions & 90 deletions src/test/bench/shootout-meteor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
Expand All @@ -8,11 +8,18 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(phase)]
#[phase(syntax)] extern crate green;
extern crate sync;

use sync::Arc;

green_start!(main)

//
// Utilities.
//


// returns an infinite iterator of repeated applications of f to x,
// i.e. [x, f(x), f(f(x)), ...], as haskell iterate function.
fn iterate<'a, T>(x: T, f: |&T|: 'a -> T) -> Iterate<'a, T> {
Expand Down Expand Up @@ -93,7 +100,7 @@ fn transform(piece: Vec<(int, int)> , all: bool) -> Vec<Vec<(int, int)>> {
// Takes a piece with minimum coordinate (0, 0) (as generated by
// transform). Returns the corresponding mask if p translated by (dy,
// dx) is on the board.
fn mask(dy: int, dx: int, id: uint, p: &[(int, int)]) -> Option<u64> {
fn mask(dy: int, dx: int, id: uint, p: &Vec<(int, int)>) -> Option<u64> {
let mut m = 1 << (50 + id);
for &(y, x) in p.iter() {
let x = x + dx + (y + (dy % 2)) / 2;
Expand All @@ -105,7 +112,7 @@ fn mask(dy: int, dx: int, id: uint, p: &[(int, int)]) -> Option<u64> {
Some(m)
}

// Makes every possible masks. masks[id][i] correspond to every
// Makes every possible masks. masks[i][id] correspond to every
// possible masks for piece with identifier id with minimum coordinate
// (i/5, i%5).
fn make_masks() -> Vec<Vec<Vec<u64> > > {
Expand All @@ -120,168 +127,182 @@ fn make_masks() -> Vec<Vec<Vec<u64> > > {
vec!((0,0),(0,1),(0,2),(1,0),(1,2)),
vec!((0,0),(0,1),(0,2),(1,2),(1,3)),
vec!((0,0),(0,1),(0,2),(0,3),(1,2)));
let mut res = Vec::new();
for (id, p) in pieces.move_iter().enumerate() {
// To break the central symetry of the problem, every
// transformation must be taken except for one piece (piece 3
// here).
let trans = transform(p, id != 3);
let mut cur_piece = Vec::new();
for dy in range(0, 10) {
for dx in range(0, 5) {
let masks =
trans.iter()
.filter_map(|t| mask(dy, dx, id, t.as_slice()))
.collect();
cur_piece.push(masks);
}
}
res.push(cur_piece);
}
res

// To break the central symetry of the problem, every
// transformation must be taken except for one piece (piece 3
// here).
let transforms: Vec<Vec<Vec<(int, int)>>> =
pieces.move_iter().enumerate()
.map(|(id, p)| transform(p, id != 3))
.collect();

range(0, 50).map(|yx| {
transforms.iter().enumerate().map(|(id, t)| {
t.iter().filter_map(|p| mask(yx / 5, yx % 5, id, p)).collect()
}).collect()
}).collect()
}

// Check if all coordinates can be covered by an unused piece and that
// all unused piece can be placed on the board.
fn is_board_unfeasible(board: u64, masks: &[Vec<Vec<u64> > ]) -> bool {
fn is_board_unfeasible(board: u64, masks: &Vec<Vec<Vec<u64>>>) -> bool {
let mut coverable = board;
for i in range(0, 50).filter(|&i| board & 1 << i == 0) {
for (cur_id, pos_masks) in masks.iter().enumerate() {
if board & 1 << (50 + cur_id) != 0 {continue;}
for &cur_m in pos_masks.get(i as uint).iter() {
if cur_m & board == 0 {coverable |= cur_m;}
for (i, masks_at) in masks.iter().enumerate() {
if board & 1 << i != 0 { continue; }
for (cur_id, pos_masks) in masks_at.iter().enumerate() {
if board & 1 << (50 + cur_id) != 0 { continue; }
for &cur_m in pos_masks.iter() {
if cur_m & board != 0 { continue; }
coverable |= cur_m;
// if every coordinates can be covered and every
// piece can be used.
if coverable == (1 << 60) - 1 { return false; }
}
}
if coverable & (1 << i) == 0 {return true;}
if coverable & 1 << i == 0 { return true; }
}
// check if every coordinates can be covered and every piece can
// be used.
coverable != (1 << 60) - 1
true
}

// Filter the masks that we can prove to result to unfeasible board.
fn filter_masks(masks: &[Vec<Vec<u64> > ]) -> Vec<Vec<Vec<u64> > > {
masks.iter().map(
|p| p.iter().map(
|p| p.iter()
.map(|&m| m)
fn filter_masks(masks: &mut Vec<Vec<Vec<u64>>>) {
for i in range(0, masks.len()) {
for j in range(0, masks.get(i).len()) {
*masks.get_mut(i).get_mut(j) =
masks.get(i).get(j).iter().map(|&m| m)
.filter(|&m| !is_board_unfeasible(m, masks))
.collect())
.collect())
.collect()
.collect();
}
}
}

// Gets the identifier of a mask.
fn get_id(m: u64) -> u8 {
for id in range(0, 10) {
if m & (1 << (id + 50)) != 0 {return id as u8;}
for id in range(0u8, 10) {
if m & (1 << (id + 50)) != 0 {return id;}
}
fail!("{:016x} does not have a valid identifier", m);
}

// Converts a list of mask to a ~str.
fn to_utf8(raw_sol: &List<u64>) -> ~str {
let mut sol: Vec<u8> = Vec::from_elem(50, '.' as u8);
fn to_vec(raw_sol: &List<u64>) -> Vec<u8> {
let mut sol = Vec::from_elem(50, '.' as u8);
for &m in raw_sol.iter() {
let id = get_id(m);
for i in range(0, 50) {
let id = '0' as u8 + get_id(m);
for i in range(0u, 50) {
if m & 1 << i != 0 {
*sol.get_mut(i as uint) = '0' as u8 + id;
*sol.get_mut(i) = id;
}
}
}
std::str::from_utf8(sol.as_slice()).unwrap().to_owned()
sol
}

// Prints a solution in ~str form.
fn print_sol(sol: &str) {
for (i, c) in sol.chars().enumerate() {
fn print_sol(sol: &Vec<u8>) {
for (i, c) in sol.iter().enumerate() {
if (i) % 5 == 0 { println!(""); }
if (i + 5) % 10 == 0 { print!(" "); }
print!("{} ", c);
print!("{} ", *c as char);
}
println!("");
}

// The data managed during the search
struct Data {
// If more than stop_after is found, stop the search.
stop_after: int,
// Number of solution found.
nb: int,
// Lexicographically minimal solution found.
min: ~str,
min: Vec<u8>,
// Lexicographically maximal solution found.
max: ~str
max: Vec<u8>
}
impl Data {
fn new() -> Data {
Data {nb: 0, min: vec!(), max: vec!()}
}
fn reduce_from(&mut self, other: Data) {
self.nb += other.nb;
let Data { min: min, max: max, ..} = other;
if min < self.min { self.min = min; }
if max > self.max { self.max = max; }
}
}

// Records a new found solution. Returns false if the search must be
// stopped.
fn handle_sol(raw_sol: &List<u64>, data: &mut Data) -> bool {
fn handle_sol(raw_sol: &List<u64>, data: &mut Data) {
// because we break the symetry, 2 solutions correspond to a call
// to this method: the normal solution, and the same solution in
// reverse order, i.e. the board rotated by half a turn.
data.nb += 2;
let sol1 = to_utf8(raw_sol);
let sol2: ~str = sol1.chars().rev().collect();
let sol1 = to_vec(raw_sol);
let sol2: Vec<u8> = sol1.iter().rev().map(|x| *x).collect();

if data.nb == 2 {
data.min = sol1.clone();
data.max = sol1.clone();
}

if sol1 < data.min {data.min = sol1.clone();}
if sol2 < data.min {data.min = sol2.clone();}
if sol1 > data.max {data.max = sol1;}
if sol2 > data.max {data.max = sol2;}
data.nb < data.stop_after
if sol1 < data.min {data.min = sol1;}
else if sol1 > data.max {data.max = sol1;}
if sol2 < data.min {data.min = sol2;}
else if sol2 > data.max {data.max = sol2;}
}

// Search for every solutions. Returns false if the search was
// stopped before the end.
fn search(
masks: &[Vec<Vec<u64> > ],
masks: &Vec<Vec<Vec<u64>>>,
board: u64,
mut i: int,
mut i: uint,
cur: List<u64>,
data: &mut Data)
-> bool
{
// Search for the lesser empty coordinate.
while board & (1 << i) != 0 && i < 50 {i += 1;}
// the board is full: a solution is found.
if i >= 50 {return handle_sol(&cur, data);}
let masks_at = masks.get(i);

// for every unused piece
for id in range(0, 10).filter(|id| board & (1 << (id + 50)) == 0) {
for id in range(0u, 10).filter(|id| board & (1 << (id + 50)) == 0) {
// for each mask that fits on the board
for &m in masks[id as uint].get(i as uint)
.iter()
.filter(|&m| board & *m == 0) {
for &m in masks_at.get(id).iter().filter(|&m| board & *m == 0) {
// This check is too costy.
//if is_board_unfeasible(board | m, masks) {continue;}
if !search(masks, board | m, i + 1, Cons(m, &cur), data) {
return false;
}
search(masks, board | m, i + 1, Cons(m, &cur), data);
}
}
return true;
}

fn par_search(masks: Vec<Vec<Vec<u64>>>) -> Data {
let masks = Arc::new(masks);
let (tx, rx) = channel();

// launching the search in parallel on every masks at minimum
// coordinate (0,0)
for &m in masks.get(0).iter().flat_map(|masks_pos| masks_pos.iter()) {
let masks = masks.clone();
let tx = tx.clone();
spawn(proc() {
let mut data = Data::new();
search(&*masks, m, 1, Cons(m, &Nil), &mut data);
tx.send(data);
});
}

// collecting the results
drop(tx);
let mut data = rx.recv();
for d in rx.iter() { data.reduce_from(d); }
data
}

fn main () {
let args = std::os::args();
let args = args.as_slice();
let stop_after = if args.len() <= 1 {
2098
} else {
from_str(args[1]).unwrap()
};
let masks = make_masks();
let masks = filter_masks(masks.as_slice());
let mut data = Data {stop_after: stop_after, nb: 0, min: "".to_owned(), max: "".to_owned()};
search(masks.as_slice(), 0, 0, Nil, &mut data);
let mut masks = make_masks();
filter_masks(&mut masks);
let data = par_search(masks);
println!("{} solutions found", data.nb);
print_sol(data.min);
print_sol(data.max);
print_sol(&data.min);
print_sol(&data.max);
println!("");
}