rust/src/test/bench/shootout-k-nucleotide.rs
2013-05-03 20:01:42 -04:00

316 lines
7.6 KiB
Rust

// xfail-test
extern mod std;
use core::cast::transmute;
use core::i32::range;
use core::libc::{STDIN_FILENO, c_int, fdopen, fgets, fileno, fopen, fstat};
use core::libc::{stat, strlen};
use core::ptr::null;
use core::unstable::intrinsics::init;
use core::vec::{reverse, slice};
use std::sort::quick_sort3;
static LINE_LEN: uint = 80;
static TABLE: [u8, ..4] = [ 'A' as u8, 'C' as u8, 'G' as u8, 'T' as u8 ];
static TABLE_SIZE: uint = 2 << 16;
static OCCURRENCES: [&'static str, ..5] = [
"GGT",
"GGTA",
"GGTATT",
"GGTATTTTAATT",
"GGTATTTTAATTTATAGT",
];
// Code implementation
#[deriving(Eq, Ord)]
struct Code(u64);
impl Code {
fn hash(&self) -> u64 {
**self
}
#[inline(always)]
fn push_char(&self, c: u8) -> Code {
Code((**self << 2) + (pack_symbol(c) as u64))
}
fn rotate(&self, c: u8, frame: i32) -> Code {
Code(*self.push_char(c) & ((1u64 << (2 * (frame as u64))) - 1))
}
fn pack(string: &str) -> Code {
let mut code = Code(0u64);
for uint::range(0, string.len()) |i| {
code = code.push_char(string[i]);
}
code
}
// XXX: Inefficient.
fn unpack(&self, frame: i32) -> ~str {
let mut key = **self;
let mut result = ~[];
for (frame as uint).times {
result.push(unpack_symbol((key as u8) & 3));
key >>= 2;
}
reverse(result);
str::from_bytes(result)
}
}
// Hash table implementation
trait TableCallback {
fn f(&self, entry: &mut Entry);
}
struct BumpCallback;
impl TableCallback for BumpCallback {
fn f(&self, entry: &mut Entry) {
entry.count += 1;
}
}
struct PrintCallback(&'static str);
impl TableCallback for PrintCallback {
fn f(&self, entry: &mut Entry) {
println(fmt!("%d\t%s", entry.count as int, **self));
}
}
struct Entry {
code: Code,
count: i32,
next: Option<~Entry>,
}
struct Table {
count: i32,
items: [Option<~Entry>, ..TABLE_SIZE]
}
impl Table {
fn new() -> Table {
Table {
count: 0,
items: [ None, ..TABLE_SIZE ],
}
}
fn search_remainder<C:TableCallback>(item: &mut Entry, key: Code, c: C) {
match item.next {
None => {
let mut entry = ~Entry {
code: key,
count: 0,
next: None,
};
c.f(entry);
item.next = Some(entry);
}
Some(ref mut entry) => {
if entry.code == key {
c.f(*entry);
return;
}
Table::search_remainder(*entry, key, c)
}
}
}
fn lookup<C:TableCallback>(&mut self, key: Code, c: C) {
let index = *key % (TABLE_SIZE as u64);
{
if self.items[index].is_none() {
let mut entry = ~Entry {
code: key,
count: 0,
next: None,
};
c.f(entry);
self.items[index] = Some(entry);
return;
}
}
{
let mut entry = &mut *self.items[index].get_mut_ref();
if entry.code == key {
c.f(*entry);
return;
}
Table::search_remainder(*entry, key, c)
}
}
fn each(&self, f: &fn(entry: &Entry) -> bool) {
for self.items.each |item| {
match *item {
None => {}
Some(ref item) => {
let mut item: &Entry = *item;
loop {
if !f(item) {
return;
}
match item.next {
None => break,
Some(ref next_item) => item = &**next_item,
}
}
}
};
}
}
}
// Main program
fn pack_symbol(c: u8) -> u8 {
match c {
'a' as u8 | 'A' as u8 => 0,
'c' as u8 | 'C' as u8 => 1,
'g' as u8 | 'G' as u8 => 2,
't' as u8 | 'T' as u8 => 3,
_ => fail!(c.to_str())
}
}
fn unpack_symbol(c: u8) -> u8 {
TABLE[c]
}
fn next_char<'a>(mut buf: &'a [u8]) -> &'a [u8] {
loop {
buf = slice(buf, 1, buf.len());
if buf.len() == 0 {
break;
}
if buf[0] != (' ' as u8) && buf[0] != ('\t' as u8) &&
buf[0] != ('\n' as u8) && buf[0] != 0 {
break;
}
}
buf
}
#[inline(never)]
fn read_stdin() -> ~[u8] {
unsafe {
let mode = "r";
//let stdin = fdopen(STDIN_FILENO as c_int, transmute(&mode[0]));
let path = "knucleotide-input.txt";
let stdin = fopen(transmute(&path[0]), transmute(&mode[0]));
let mut st: stat = init();
fstat(fileno(stdin), &mut st);
let mut buf = vec::from_elem(st.st_size as uint, 0);
let header = str::byte_slice_no_callback(">THREE");
let header = vec::slice(header, 0, 6);
{
let mut window: &mut [u8] = buf;
loop {
fgets(transmute(&mut window[0]), LINE_LEN as c_int, stdin);
{
if vec::slice(window, 0, 6) == header {
break;
}
}
}
while fgets(transmute(&mut window[0]),
LINE_LEN as c_int,
stdin) != null() {
window = vec::mut_slice(window,
strlen(transmute(&window[0])) as uint,
window.len());
}
}
buf
}
}
#[inline(never)]
#[fixed_stack_segment]
fn generate_frequencies(frequencies: &mut Table,
mut input: &[u8],
frame: i32) {
let mut code = Code(0);
// Pull first frame.
for (frame as uint).times {
code = code.push_char(input[0]);
input = next_char(input);
}
frequencies.lookup(code, BumpCallback);
while input.len() != 0 && input[0] != ('>' as u8) {
code = code.rotate(input[0], frame);
frequencies.lookup(code, BumpCallback);
input = next_char(input);
}
}
#[inline(never)]
#[fixed_stack_segment]
fn print_frequencies(frequencies: &Table, frame: i32) {
let mut vector = ~[];
for frequencies.each |entry| {
vector.push((entry.code, entry.count));
}
quick_sort3(vector);
let mut total_count = 0;
for vector.each |&(_, count)| {
total_count += count;
}
for vector.each |&(key, count)| {
println(fmt!("%s %.3f",
key.unpack(frame),
(count as float * 100.0) / (total_count as float)));
}
}
fn print_occurrences(frequencies: &mut Table, occurrence: &'static str) {
frequencies.lookup(Code::pack(occurrence), PrintCallback(occurrence))
}
#[fixed_stack_segment]
fn main() {
let input = read_stdin();
let mut frequencies = ~Table::new();
generate_frequencies(frequencies, input, 1);
print_frequencies(frequencies, 1);
*frequencies = Table::new();
generate_frequencies(frequencies, input, 2);
print_frequencies(frequencies, 2);
for range(0, 5) |i| {
let occurrence = OCCURRENCES[i];
*frequencies = Table::new();
generate_frequencies(frequencies,
input,
occurrence.len() as i32);
print_occurrences(frequencies, occurrence);
}
}