<commit_before>// All data taken from www.brucelindbloom.com.

use num::{cast, Float};
use channel::FreeChannelScalar;
use xyz::Xyz;

pub trait NamedWhitePoint<T> {
    fn get_xyz() -> Xyz<T>;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct A;
impl<T> NamedWhitePoint<T> for A
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(1.09850).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.35585).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct B;
impl<T> NamedWhitePoint<T> for B
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.99072).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.85223).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct C;
impl<T> NamedWhitePoint<T> for C
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.98074).unwrap(),
                           cast(1.0).unwrap(),
                           cast(1.18232).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct D50;
impl<T> NamedWhitePoint<T> for D50
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.96422).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.82521).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct D55;
impl<T> NamedWhitePoint<T> for D55
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.95682).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.92149).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct D65;
impl<T> NamedWhitePoint<T> for D65
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.95047).unwrap(),
                           cast(1.0).unwrap(),
                           cast(1.08883).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct D75;
impl<T> NamedWhitePoint<T> for D75
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.94972).unwrap(),
                           cast(1.0).unwrap(),
                           cast(1.22638).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct E;
impl<T> NamedWhitePoint<T> for E
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(1.0).unwrap(), cast(1.0).unwrap(), cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct F2;
impl<T> NamedWhitePoint<T> for F2
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.99186).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.67393).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct F7;
impl<T> NamedWhitePoint<T> for F7
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.95041).unwrap(),
                           cast(1.0).unwrap(),
                           cast(1.08747).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct F11;
impl<T> NamedWhitePoint<T> for F11
    where T: Float + FreeChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(1.00962).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.64350).unwrap())
    }
}
<commit_msg>Added xyY values for all white points.<commit_after>// All data taken from www.brucelindbloom.com.

use num::{cast, Float};
use channel::{FreeChannelScalar, PosNormalChannelScalar};
use xyz::Xyz;
use xyy::XyY;

pub trait NamedWhitePoint<T> {
    fn get_xyz() -> Xyz<T>;
    fn get_xy_chromaticity() -> XyY<T>;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct A;
impl<T> NamedWhitePoint<T> for A
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(1.09850).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.35585).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(0.44757).unwrap(),
                           cast(0.40745).unwrap(),
                           cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct B;
impl<T> NamedWhitePoint<T> for B
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.99072).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.85223).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(0.34842).unwrap(),
                           cast(0.35161).unwrap(),
                           cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct C;
impl<T> NamedWhitePoint<T> for C
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.98074).unwrap(),
                           cast(1.0).unwrap(),
                           cast(1.18232).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(0.31006).unwrap(),
                           cast(0.31616).unwrap(),
                           cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct D50;
impl<T> NamedWhitePoint<T> for D50
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.96422).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.82521).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(0.34567).unwrap(),
                           cast(0.35850).unwrap(),
                           cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct D55;
impl<T> NamedWhitePoint<T> for D55
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.95682).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.92149).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(0.33242).unwrap(),
                           cast(0.34743).unwrap(),
                           cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct D65;
impl<T> NamedWhitePoint<T> for D65
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.95047).unwrap(),
                           cast(1.0).unwrap(),
                           cast(1.08883).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(0.31271).unwrap(),
                           cast(0.32902).unwrap(),
                           cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct D75;
impl<T> NamedWhitePoint<T> for D75
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.94972).unwrap(),
                           cast(1.0).unwrap(),
                           cast(1.22638).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(0.29902).unwrap(),
                           cast(0.31485).unwrap(),
                           cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct E;
impl<T> NamedWhitePoint<T> for E
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(1.0).unwrap(), cast(1.0).unwrap(), cast(1.0).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(1.0 / 3.0).unwrap(),
                           cast(1.0 / 3.0).unwrap(),
                           cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct F2;
impl<T> NamedWhitePoint<T> for F2
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.99186).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.67393).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(0.37208).unwrap(),
                           cast(0.37529).unwrap(),
                           cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct F7;
impl<T> NamedWhitePoint<T> for F7
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(0.95041).unwrap(),
                           cast(1.0).unwrap(),
                           cast(1.08747).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(0.31292).unwrap(),
                           cast(0.32933).unwrap(),
                           cast(1.0).unwrap())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct F11;
impl<T> NamedWhitePoint<T> for F11
    where T: Float + FreeChannelScalar + PosNormalChannelScalar
{
    #[inline]
    fn get_xyz() -> Xyz<T> {
        Xyz::from_channels(cast(1.00962).unwrap(),
                           cast(1.0).unwrap(),
                           cast(0.64350).unwrap())
    }
    #[inline]
    fn get_xy_chromaticity() -> XyY<T> {
        XyY::from_channels(cast(0.38052).unwrap(),
                           cast(0.37713).unwrap(),
                           cast(1.0).unwrap())
    }
}
<|endoftext|>


<commit_before>use fftw::plan::*;
use fftw::types::*;

/// Check successive forward and backward transform equals to the identity
#[test]
fn r2r2r_identity() {
    let n = 32;
    let mut a = vec![0.0f64; n];
    let mut b = vec![0.0f64; n];

    // http://www.fftw.org/fftw3_doc/Real_002dto_002dReal-Transform-Kinds.html,
    // contains the normalization rules for each R2R transform. Given
    // normalization `factor`, we can test `inverse_dct(dct(x / factor)) == x`.
    // If you want to apply the normalization symmetrically in the transform
    // and inverse, then simply divide all elements of the output (or input)
    // vector by `sqrt(factor)`, both on the forward and inverse passes. We
    // demonstrate this here.

    // Here we use a type-2 DCT, whose inverse is a type-3 DCT, and whose
    // normalization is `factor=2*n`.
    let mut fwd: R2RPlan64 =
        R2RPlan::new(&[n], &mut a, &mut b, R2RKind::FFTW_REDFT10, Flag::MEASURE).unwrap();
    let mut bwd: R2RPlan64 =
        R2RPlan::new(&[n], &mut b, &mut a, R2RKind::FFTW_REDFT01, Flag::MEASURE).unwrap();
    let factor = 2. * n as f64;

    // Vector of ones.
    a = vec![1.0f64; n];

    // Forward pass.
    fwd.r2r(&mut a, &mut b).unwrap();
    // Renormalize
    for i in &mut b {
        *i /= factor.sqrt();
    }

    // Inverse.
    bwd.r2r(&mut b, &mut a).unwrap();
    // Renormalize.
    for i in &mut a {
        *i /= factor.sqrt();
    }

    // Ensure we have the original vector of ones.
    for v in a.iter() {
        let dif = (v - 1.).abs();
        if dif > 1e-7 {
            panic!("Large difference: v={}, dif={}", v, dif);
        }
    }
}
<commit_msg>Fix r2r2r_identity test<commit_after>use fftw::plan::*;
use fftw::types::*;

/// Check successive forward and backward transform equals to the identity
#[test]
fn r2r2r_identity() {
    let n = 32;
    let mut a = vec![0.0f64; n];
    let mut b = vec![0.0f64; n];

    // http://www.fftw.org/fftw3_doc/Real_002dto_002dReal-Transform-Kinds.html,
    // contains the normalization rules for each R2R transform. Given
    // normalization `factor`, we can test `inverse_dct(dct(x / factor)) == x`.
    // If you want to apply the normalization symmetrically in the transform
    // and inverse, then simply divide all elements of the output (or input)
    // vector by `sqrt(factor)`, both on the forward and inverse passes. We
    // demonstrate this here.

    // Here we use a type-2 DCT, whose inverse is a type-3 DCT, and whose
    // normalization is `factor=2*n`.
    let mut fwd: R2RPlan64 =
        R2RPlan::new(&[n], &mut a, &mut b, R2RKind::FFTW_REDFT10, Flag::MEASURE).unwrap();
    let mut bwd: R2RPlan64 =
        R2RPlan::new(&[n], &mut b, &mut a, R2RKind::FFTW_REDFT01, Flag::MEASURE).unwrap();
    let factor = 2. * n as f64;

    // Vector of ones.
    for a_i in a.iter_mut() {
        *a_i = 1.0;
    }

    // Forward pass.
    fwd.r2r(&mut a, &mut b).unwrap();
    // Renormalize
    for i in &mut b {
        *i /= factor.sqrt();
    }

    // Inverse.
    bwd.r2r(&mut b, &mut a).unwrap();
    // Renormalize.
    for i in &mut a {
        *i /= factor.sqrt();
    }

    // Ensure we have the original vector of ones.
    for v in a.iter() {
        let dif = (v - 1.).abs();
        if dif > 1e-7 {
            panic!("Large difference: v={}, dif={}", v, dif);
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::io::Write;

use bufstream::BufStream;

use common::consts;
use protocol::cmd::Cmd;
use protocol::cmd::Delete;
use protocol::cmd::Get;
use protocol::cmd::Resp;
use protocol::cmd::Set;
use protocol::cmd::SetInstr;

use super::conversions::as_number;
use super::conversions::as_string;
use super::errors::TcpTransportError;
use super::stats::TransportStats;
use super::typedefs::TcpTransportResult;

pub struct TcpTransport<T: Read + Write> {
    stream: BufStream<T>,

    pub line_buffer: Vec<u8>,
    pub line_cursor: usize,
    pub line_break_pos: usize,

    key_maxlen: u64,

    stats: TransportStats,
}

impl<T: Read + Write> TcpTransport<T> {
    pub fn new(stream: T) -> TcpTransport<T> {
        TcpTransport {
            key_maxlen: 250, // memcached standard

            // Used to read the first line of a command, which includes a
            // keyword, a key, flags and a bytecount. We don't expect it to be
            // much longer than the key itself. If it is we panic...
            line_buffer: vec![0; 250 + 100],
            line_cursor: 0,
            line_break_pos: 0,

            stats: TransportStats::new(),
            stream: BufStream::new(stream),
        }
    }

    pub fn with_key_maxlen(&mut self,
                           key_maxlen: u64)
                           -> &mut TcpTransport<T> {
        self.key_maxlen = key_maxlen;
        self
    }

    pub fn get_stats_clone(&self) -> TransportStats {
        self.stats.clone()
    }

    pub fn get_stream(&self) -> &T {
        self.stream.get_ref()
    }

    // Basic bytes manipulation and reading from the stream

    pub fn read_bytes_exact(&mut self,
                            len: u64)
                            -> TcpTransportResult<Vec<u8>> {
        let mut bytes = vec![0; len as usize];
        let mut cursor: usize = 0;
        let mut iteration = 0;

        loop {
            // Read as much as we can, hopefully the whole buffer
            let rv = self.stream.read(&mut bytes[cursor..]);

            // Something went wrong
            if rv.is_err() {
                return Err(TcpTransportError::StreamReadError);
            }

            // How much we actually read
            let bytes_cnt = rv.unwrap();

            // Woops, there was nothing to read!
            if bytes_cnt == 0 {
                if iteration == 0 {
                    // It's the first iteration, so there wasn't anything to
                    // read in the first place, we were called in vain!
                    return Err(TcpTransportError::StreamReadError);

                } else {
                    // It turns out we read the very last byte on the last
                    // iteration, so nothing more to do at this point
                    break;
                }
            }

            // We advance the position in the buffer for next iteration
            cursor += bytes_cnt;

            // Update stats
            self.stats.bytes_read += bytes_cnt as u64;

            // We've read as much as was requested already
            if (bytes_cnt as u64) >= len {
                break;
            }

            iteration += 1;
        }

        if (cursor as u64) < len {
            bytes.truncate(cursor);
        }

        Ok(bytes)
    }

    pub fn read_word_in_line(&mut self) -> TcpTransportResult<(Vec<u8>, bool)> {
        let mut word = vec![];
        let mut byte = [0; 1];
        let mut end_of_line = false;

        loop {
            // Read a byte
            let rv = self.stream.read(&mut byte);

            // If there was an error or if there was nothing to read we bail
            if rv.is_err() || rv.unwrap() == 0 {
                return Err(TcpTransportError::StreamReadError);
            }

            // Update stats
            self.stats.bytes_read += 1;

            if byte[0] == consts::BYTE_SPACE {
                // We found a space

                if word.is_empty() {
                    // If it's one or more leading space we ignore it
                    continue;
                }

                // All good, we've found the end of the word
                break;

            } else if byte[0] == consts::BYTE_CARRIAGE_RETURN {
                // We found \r, we think it's the end of the line

                // Try to read \n
                let rv = self.stream.read(&mut byte);

                // If there was an error or if there was nothing to read we bail
                if rv.is_err() || rv.unwrap() == 0 {
                    return Err(TcpTransportError::StreamReadError);
                }

                // Update stats
                self.stats.bytes_read += 1;

                // If it's not a correct end of line we storm out in protest
                if byte[0] != consts::BYTE_LINE_FEED {
                    return Err(TcpTransportError::LineReadError);
                }

                // Else it's all good, we've read the whole line including the
                // terminator
                end_of_line = true;
                break;

            } else {
                // It's not a special char, append to our word
                word.push(byte[0]);
            }
        }

        Ok((word, end_of_line))
    }

    pub fn read_line_as_words(&mut self) -> TcpTransportResult<Vec<Vec<u8>>> {
        let mut words = vec![];

        loop {
            let (word, end_of_line) = try!(self.read_word_in_line());

            // Don't bother if it's an empty word (trailing space before \r\n)
            if !word.is_empty() {
                words.push(word);
            }

            if end_of_line {
                break;
            }
        }

        Ok(words)
    }

    // Writing to the stream

    pub fn flush_writes(&mut self) -> TcpTransportResult<()> {
        match self.stream.flush() {
            Ok(_) => Ok(()),
            Err(_) => Err(TcpTransportError::StreamWriteError),
        }
    }

    pub fn write_bytes(&mut self,
                       bytes: &Vec<u8>)
                       -> TcpTransportResult<usize> {
        match self.stream.write(bytes) {
            Ok(cnt_written) => {
                // Update stats
                self.stats.bytes_written += cnt_written as u64;

                Ok(cnt_written)
            }
            Err(_) => Err(TcpTransportError::StreamWriteError),
        }
    }

    pub fn write_string(&mut self, string: &str) -> TcpTransportResult<usize> {
        let bytes = string.to_string().into_bytes();
        Ok(try!(self.write_bytes(&bytes)))
    }

    // Parse individual commands

    pub fn parse_cmd_delete(&mut self) -> TcpTransportResult<Cmd> {
        // parse the key
        let key_str = {
            let (key, end_of_line) = try!(self.read_word_in_line());

            if end_of_line {
                return Err(TcpTransportError::CommandParseError);
            }

            try!(as_string(key))
        };

        // parse noreply
        let noreply_flag = {
            let (noreply, end_of_line) = try!(self.read_word_in_line());

            if !end_of_line {
                return Err(TcpTransportError::CommandParseError);
            }

            let noreply_str = try!(as_string(noreply));
            match noreply_str == "noreply" {
                true => true,
                false => false,
            }
        };

        Ok(Cmd::Delete(Delete {
            key: key_str,
            noreply: noreply_flag,
        }))
    }

    pub fn parse_cmd_get(&mut self) -> TcpTransportResult<Cmd> {
        let mut keys = vec![];

        loop {
            let (key, end_of_line) = try!(self.read_word_in_line());
            let key_str = try!(as_string(key));
            keys.push(key_str);

            if end_of_line {
                break;
            }
        }

        Ok(Cmd::Get(Get { keys: keys }))
    }

    pub fn parse_cmd_set(&mut self) -> TcpTransportResult<Cmd> {
        // parse the key
        let key_str = {
            let (key, end_of_line) = try!(self.read_word_in_line());

            if end_of_line {
                return Err(TcpTransportError::CommandParseError);
            }

            try!(as_string(key))
        };

        // parse the flags
        let flags_num = {
            let (flags, end_of_line) = try!(self.read_word_in_line());

            if end_of_line {
                return Err(TcpTransportError::CommandParseError);
            }

            try!(as_number::<u16>(flags))
        };

        // parse the exptime
        let exptime_num = {
            let (exptime, end_of_line) = try!(self.read_word_in_line());

            if end_of_line {
                return Err(TcpTransportError::CommandParseError);
            }

            try!(as_number::<u32>(exptime))
        };

        // parse the bytelen
        let bytelen_num = {
            let (bytelen, end_of_line) = try!(self.read_word_in_line());

            if end_of_line {
                return Err(TcpTransportError::CommandParseError);
            }

            try!(as_number::<u64>(bytelen))
        };

        // parse noreply
        let noreply_flag = {
            let (noreply, end_of_line) = try!(self.read_word_in_line());

            if !end_of_line {
                return Err(TcpTransportError::CommandParseError);
            }

            let noreply_str = try!(as_string(noreply));
            match noreply_str == "noreply" {
                true => true,
                false => false,
            }
        };

        // We now know the byte length, so read the value
        let value = try!(self.read_bytes_exact(bytelen_num));

        // The value is the wrong size
        if value.len() as u64 != bytelen_num {
            return Err(TcpTransportError::CommandParseError);
        }

        // Verify that we found the line terminator
        let terminator = try!(self.read_bytes_exact(2));
        if !terminator.ends_with(&[consts::BYTE_CARRIAGE_RETURN,
                                   consts::BYTE_LINE_FEED]) {
            return Err(TcpTransportError::CommandParseError);
        }

        // We got all the values we expected and there is nothing left
        return Ok(Cmd::Set(Set {
            instr: SetInstr::Set,
            key: key_str,
            flags: flags_num,
            exptime: exptime_num,
            data: value,
            noreply: noreply_flag,
        }));
    }

    // High level functions

    pub fn read_cmd(&mut self) -> TcpTransportResult<Cmd> {
        let keyword_str = {
            let (word, end_of_line) = try!(self.read_word_in_line());
            try!(as_string(word))
        };

        if keyword_str == "get" {
            return self.parse_cmd_get();
        } else if keyword_str == "set" {
            return self.parse_cmd_set();
        } else if keyword_str == "delete" {
            return self.parse_cmd_delete();
        } else if keyword_str == "stats" {
            // TODO check for eol since nothing follows the keyword
            return Ok(Cmd::Stats);
        } else if keyword_str == "version" {
            // TODO check for eol since nothing follows the keyword
            return Ok(Cmd::Version);
        } else if keyword_str == "quit" {
            // TODO check for eol since nothing follows the keyword
            return Ok(Cmd::Quit);
        }

        Err(TcpTransportError::InvalidCmd)
    }

    pub fn write_resp(&mut self, resp: &Resp) -> TcpTransportResult<()> {
        match *resp {
            Resp::Empty => (),
            Resp::Deleted => {
                try!(self.write_string("DELETED\r\n"));
            }
            Resp::Error => {
                try!(self.write_string("ERROR\r\n"));
            }
            Resp::NotFound => {
                try!(self.write_string("NOT_FOUND\r\n"));
            }
            Resp::Stats(ref stats) => {
                for stat in stats {
                    try!(self.write_string("STAT "));
                    try!(self.write_string(&stat.key));
                    try!(self.write_string(" "));
                    try!(self.write_string(&stat.value));
                    try!(self.write_string("\r\n"));
                }
                try!(self.write_string("END\r\n"));
            }
            Resp::Stored => {
                try!(self.write_string("STORED\r\n"));
            }
            Resp::Values(ref values) => {
                for value in values {
                    try!(self.write_string("VALUE ")); // keyword
                    try!(self.write_string(&value.key)); // key
                    try!(self.write_string(" ")); // space
                    try!(self.write_string(&value.flags.to_string())); // flags
                    try!(self.write_string(" ")); // space
                    try!(self.write_string(&value.data.len().to_string())); // bytelen
                    try!(self.write_string(&"\r\n".to_string())); // newline
                    try!(self.write_bytes(&value.data)); // data block
                    try!(self.write_string(&"\r\n".to_string())); // newline
                }
                try!(self.write_string(&"END\r\n".to_string())); // END + newline
            }
            Resp::Version(ref version) => {
                try!(self.write_string("VERSION "));
                try!(self.write_string(&version)); // key
                try!(self.write_string(&"\r\n".to_string())); // newline
            }
            _ => {
                return Err(TcpTransportError::StreamWriteError);
            }
        }

        // Make sure all bytes were actually sent
        self.flush_writes()
    }
}
<commit_msg>add macro to get rid of wordy if statements<commit_after>use std::io::Read;
use std::io::Write;

use bufstream::BufStream;

use common::consts;
use protocol::cmd::Cmd;
use protocol::cmd::Delete;
use protocol::cmd::Get;
use protocol::cmd::Resp;
use protocol::cmd::Set;
use protocol::cmd::SetInstr;

use super::conversions::as_number;
use super::conversions::as_string;
use super::errors::TcpTransportError;
use super::stats::TransportStats;
use super::typedefs::TcpTransportResult;

// return_err_if!(end_of_line, TcpTransportError::StreamReadError) =>
//
// if <cond> {
//     return Err(<err>)
// }
macro_rules! return_err_if {
    ( $cond:expr, $val:expr ) => {
        {
            if $cond {
                return Err($val);
            }
        }
    };
}

pub struct TcpTransport<T: Read + Write> {
    stream: BufStream<T>,

    pub line_buffer: Vec<u8>,
    pub line_cursor: usize,
    pub line_break_pos: usize,

    key_maxlen: u64,

    stats: TransportStats,
}

impl<T: Read + Write> TcpTransport<T> {
    pub fn new(stream: T) -> TcpTransport<T> {
        TcpTransport {
            key_maxlen: 250, // memcached standard

            // Used to read the first line of a command, which includes a
            // keyword, a key, flags and a bytecount. We don't expect it to be
            // much longer than the key itself. If it is we panic...
            line_buffer: vec![0; 250 + 100],
            line_cursor: 0,
            line_break_pos: 0,

            stats: TransportStats::new(),
            stream: BufStream::new(stream),
        }
    }

    pub fn with_key_maxlen(&mut self,
                           key_maxlen: u64)
                           -> &mut TcpTransport<T> {
        self.key_maxlen = key_maxlen;
        self
    }

    pub fn get_stats_clone(&self) -> TransportStats {
        self.stats.clone()
    }

    pub fn get_stream(&self) -> &T {
        self.stream.get_ref()
    }

    // Basic bytes manipulation and reading from the stream

    pub fn read_bytes_exact(&mut self,
                            len: u64)
                            -> TcpTransportResult<Vec<u8>> {
        let mut bytes = vec![0; len as usize];
        let mut cursor: usize = 0;
        let mut iteration = 0;

        loop {
            // Read as much as we can, hopefully the whole buffer
            let rv = self.stream.read(&mut bytes[cursor..]);

            // Something went wrong
            if rv.is_err() {
                return Err(TcpTransportError::StreamReadError);
            }

            // How much we actually read
            let bytes_cnt = rv.unwrap();

            // Woops, there was nothing to read!
            if bytes_cnt == 0 {
                if iteration == 0 {
                    // It's the first iteration, so there wasn't anything to
                    // read in the first place, we were called in vain!
                    return Err(TcpTransportError::StreamReadError);

                } else {
                    // It turns out we read the very last byte on the last
                    // iteration, so nothing more to do at this point
                    break;
                }
            }

            // We advance the position in the buffer for next iteration
            cursor += bytes_cnt;

            // Update stats
            self.stats.bytes_read += bytes_cnt as u64;

            // We've read as much as was requested already
            if (bytes_cnt as u64) >= len {
                break;
            }

            iteration += 1;
        }

        if (cursor as u64) < len {
            bytes.truncate(cursor);
        }

        Ok(bytes)
    }

    pub fn read_word_in_line(&mut self) -> TcpTransportResult<(Vec<u8>, bool)> {
        let mut word = vec![];
        let mut byte = [0; 1];
        let mut end_of_line = false;

        loop {
            // Read a byte
            let rv = self.stream.read(&mut byte);

            // If there was an error or if there was nothing to read we bail
            if rv.is_err() || rv.unwrap() == 0 {
                return Err(TcpTransportError::StreamReadError);
            }

            // Update stats
            self.stats.bytes_read += 1;

            if byte[0] == consts::BYTE_SPACE {
                // We found a space

                if word.is_empty() {
                    // If it's one or more leading space we ignore it
                    continue;
                }

                // All good, we've found the end of the word
                break;

            } else if byte[0] == consts::BYTE_CARRIAGE_RETURN {
                // We found \r, we think it's the end of the line

                // Try to read \n
                let rv = self.stream.read(&mut byte);

                // If there was an error or if there was nothing to read we bail
                if rv.is_err() || rv.unwrap() == 0 {
                    return Err(TcpTransportError::StreamReadError);
                }

                // Update stats
                self.stats.bytes_read += 1;

                // If it's not a correct end of line we storm out in protest
                if byte[0] != consts::BYTE_LINE_FEED {
                    return Err(TcpTransportError::LineReadError);
                }

                // Else it's all good, we've read the whole line including the
                // terminator
                end_of_line = true;
                break;

            } else {
                // It's not a special char, append to our word
                word.push(byte[0]);
            }
        }

        Ok((word, end_of_line))
    }

    pub fn read_line_as_words(&mut self) -> TcpTransportResult<Vec<Vec<u8>>> {
        let mut words = vec![];

        loop {
            let (word, end_of_line) = try!(self.read_word_in_line());

            // Don't bother if it's an empty word (trailing space before \r\n)
            if !word.is_empty() {
                words.push(word);
            }

            if end_of_line {
                break;
            }
        }

        Ok(words)
    }

    // Writing to the stream

    pub fn flush_writes(&mut self) -> TcpTransportResult<()> {
        match self.stream.flush() {
            Ok(_) => Ok(()),
            Err(_) => Err(TcpTransportError::StreamWriteError),
        }
    }

    pub fn write_bytes(&mut self,
                       bytes: &Vec<u8>)
                       -> TcpTransportResult<usize> {
        match self.stream.write(bytes) {
            Ok(cnt_written) => {
                // Update stats
                self.stats.bytes_written += cnt_written as u64;

                Ok(cnt_written)
            }
            Err(_) => Err(TcpTransportError::StreamWriteError),
        }
    }

    pub fn write_string(&mut self, string: &str) -> TcpTransportResult<usize> {
        let bytes = string.to_string().into_bytes();
        Ok(try!(self.write_bytes(&bytes)))
    }

    // Parse individual commands

    pub fn parse_cmd_delete(&mut self) -> TcpTransportResult<Cmd> {
        // parse the key
        let key_str = {
            let (key, end_of_line) = try!(self.read_word_in_line());
            return_err_if!(end_of_line, TcpTransportError::CommandParseError);
            try!(as_string(key))
        };

        // parse noreply
        let noreply_flag = {
            let (noreply, end_of_line) = try!(self.read_word_in_line());
            return_err_if!(!end_of_line, TcpTransportError::CommandParseError);
            let noreply_str = try!(as_string(noreply));
            match noreply_str == "noreply" {
                true => true,
                false => false,
            }
        };

        Ok(Cmd::Delete(Delete {
            key: key_str,
            noreply: noreply_flag,
        }))
    }

    pub fn parse_cmd_get(&mut self) -> TcpTransportResult<Cmd> {
        let mut keys = vec![];

        loop {
            let (key, end_of_line) = try!(self.read_word_in_line());
            let key_str = try!(as_string(key));
            keys.push(key_str);

            if end_of_line {
                break;
            }
        }

        Ok(Cmd::Get(Get { keys: keys }))
    }

    pub fn parse_cmd_set(&mut self) -> TcpTransportResult<Cmd> {
        // parse the key
        let key_str = {
            let (key, end_of_line) = try!(self.read_word_in_line());
            return_err_if!(end_of_line, TcpTransportError::CommandParseError);
            try!(as_string(key))
        };

        // parse the flags
        let flags_num = {
            let (flags, end_of_line) = try!(self.read_word_in_line());
            return_err_if!(end_of_line, TcpTransportError::CommandParseError);
            try!(as_number::<u16>(flags))
        };

        // parse the exptime
        let exptime_num = {
            let (exptime, end_of_line) = try!(self.read_word_in_line());
            return_err_if!(end_of_line, TcpTransportError::CommandParseError);
            try!(as_number::<u32>(exptime))
        };

        // parse the bytelen
        let bytelen_num = {
            let (bytelen, end_of_line) = try!(self.read_word_in_line());
            return_err_if!(end_of_line, TcpTransportError::CommandParseError);
            try!(as_number::<u64>(bytelen))
        };

        // parse noreply
        let noreply_flag = {
            let (noreply, end_of_line) = try!(self.read_word_in_line());
            return_err_if!(!end_of_line, TcpTransportError::CommandParseError);
            let noreply_str = try!(as_string(noreply));
            match noreply_str == "noreply" {
                true => true,
                false => false,
            }
        };

        // We now know the byte length, so read the value
        let value = try!(self.read_bytes_exact(bytelen_num));

        // The value is the wrong size
        if value.len() as u64 != bytelen_num {
            return Err(TcpTransportError::CommandParseError);
        }

        // Verify that we found the line terminator
        let terminator = try!(self.read_bytes_exact(2));
        if !terminator.ends_with(&[consts::BYTE_CARRIAGE_RETURN,
                                   consts::BYTE_LINE_FEED]) {
            return Err(TcpTransportError::CommandParseError);
        }

        // We got all the values we expected and there is nothing left
        return Ok(Cmd::Set(Set {
            instr: SetInstr::Set,
            key: key_str,
            flags: flags_num,
            exptime: exptime_num,
            data: value,
            noreply: noreply_flag,
        }));
    }

    // High level functions

    pub fn read_cmd(&mut self) -> TcpTransportResult<Cmd> {
        let keyword_str = {
            let (word, end_of_line) = try!(self.read_word_in_line());
            try!(as_string(word))
        };

        if keyword_str == "get" {
            return self.parse_cmd_get();
        } else if keyword_str == "set" {
            return self.parse_cmd_set();
        } else if keyword_str == "delete" {
            return self.parse_cmd_delete();
        } else if keyword_str == "stats" {
            return Ok(Cmd::Stats);
        } else if keyword_str == "version" {
            return Ok(Cmd::Version);
        } else if keyword_str == "quit" {
            return Ok(Cmd::Quit);
        }

        Err(TcpTransportError::InvalidCmd)
    }

    pub fn write_resp(&mut self, resp: &Resp) -> TcpTransportResult<()> {
        match *resp {
            Resp::Empty => (),
            Resp::Deleted => {
                try!(self.write_string("DELETED\r\n"));
            }
            Resp::Error => {
                try!(self.write_string("ERROR\r\n"));
            }
            Resp::NotFound => {
                try!(self.write_string("NOT_FOUND\r\n"));
            }
            Resp::Stats(ref stats) => {
                for stat in stats {
                    try!(self.write_string("STAT "));
                    try!(self.write_string(&stat.key));
                    try!(self.write_string(" "));
                    try!(self.write_string(&stat.value));
                    try!(self.write_string("\r\n"));
                }
                try!(self.write_string("END\r\n"));
            }
            Resp::Stored => {
                try!(self.write_string("STORED\r\n"));
            }
            Resp::Values(ref values) => {
                for value in values {
                    try!(self.write_string("VALUE ")); // keyword
                    try!(self.write_string(&value.key)); // key
                    try!(self.write_string(" ")); // space
                    try!(self.write_string(&value.flags.to_string())); // flags
                    try!(self.write_string(" ")); // space
                    try!(self.write_string(&value.data.len().to_string())); // bytelen
                    try!(self.write_string(&"\r\n".to_string())); // newline
                    try!(self.write_bytes(&value.data)); // data block
                    try!(self.write_string(&"\r\n".to_string())); // newline
                }
                try!(self.write_string(&"END\r\n".to_string())); // END + newline
            }
            Resp::Version(ref version) => {
                try!(self.write_string("VERSION "));
                try!(self.write_string(&version)); // key
                try!(self.write_string(&"\r\n".to_string())); // newline
            }
            _ => {
                return Err(TcpTransportError::StreamWriteError);
            }
        }

        // Make sure all bytes were actually sent
        self.flush_writes()
    }
}
<|endoftext|>


<commit_before>use super::super::{CreationError, Histogram};
use tests::helpers::histo64;

#[test]
fn unit_magnitude_0_index_calculations() {
    let h = histo64(1_u64, 1_u64 << 32, 3);
    assert_eq!(2048, h.sub_bucket_count);
    assert_eq!(0, h.unit_magnitude);
    // sub_bucket_count = 2^11, so 2^11 << 22 is > the max of 2^32 for 23 buckets total
    assert_eq!(23, h.bucket_count);

    // first half of first bucket
    assert_eq!(0, h.bucket_for(3));
    assert_eq!(3, h.sub_bucket_for(3, 0));

    // second half of first bucket
    assert_eq!(0, h.bucket_for(1024 + 3));
    assert_eq!(1024 + 3, h.sub_bucket_for(1024 + 3, 0));

    // second bucket (top half)
    assert_eq!(1, h.bucket_for(2048 + 3 * 2));
    // counting by 2s, starting at halfway through the bucket
    assert_eq!(1024 + 3, h.sub_bucket_for(2048 + 3 * 2, 1));

    // third bucket (top half)
    assert_eq!(2, h.bucket_for((2048 << 1) + 3 * 4));
    // counting by 4s, starting at halfway through the bucket
    assert_eq!(1024 + 3, h.sub_bucket_for((2048 << 1) + 3 * 4, 2));

    // past last bucket -- not near u64::max_value(), so should still calculate ok.
    assert_eq!(23, h.bucket_for((2048_u64 << 22) + 3 * (1 << 23)));
    assert_eq!(1024 + 3, h.sub_bucket_for((2048_u64 << 22) + 3 * (1 << 23), 23));
}

#[test]
fn unit_magnitude_4_index_calculations() {
    let h = histo64(1_u64 << 12, 1_u64 << 32, 3);
    assert_eq!(2048, h.sub_bucket_count);
    assert_eq!(12, h.unit_magnitude);
    // sub_bucket_count = 2^11. With unit magnitude shift, it's 2^23. 2^23 << 10 is > the max of
    // 2^32 for 11 buckets total
    assert_eq!(11, h.bucket_count);
    let unit = 1_u64 << 12;

    // below lowest value
    assert_eq!(0, h.bucket_for(3));
    assert_eq!(0, h.sub_bucket_for(3, 0));

    // first half of first bucket
    assert_eq!(0, h.bucket_for(3 * unit));
    assert_eq!(3, h.sub_bucket_for(3 * unit, 0));

    // second half of first bucket
    // sub_bucket_half_count's worth of units, plus 3 more
    assert_eq!(0, h.bucket_for(unit * (1024 + 3)));
    assert_eq!(1024 + 3, h.sub_bucket_for(unit * (1024 + 3), 0));

    // second bucket (top half), bucket scale = unit << 1.
    // Middle of bucket is (sub_bucket_half_count = 2^10) of bucket scale, = unit << 11.
    // Add on 3 of bucket scale.
    assert_eq!(1, h.bucket_for((unit << 11) + 3 * (unit << 1)));
    assert_eq!(1024 + 3, h.sub_bucket_for((unit << 11) + 3 * (unit << 1), 1));

    // third bucket (top half), bucket scale = unit << 2.
    // Middle of bucket is (sub_bucket_half_count = 2^10) of bucket scale, = unit << 12.
    // Add on 3 of bucket scale.
    assert_eq!(2, h.bucket_for((unit << 12) + 3 * (unit << 2)));
    assert_eq!(1024 + 3, h.sub_bucket_for((unit << 12) + 3 * (unit << 2), 2));

    // past last bucket -- not near u64::max_value(), so should still calculate ok.
    assert_eq!(11, h.bucket_for((unit << 21) + 3 * (unit << 11)));
    assert_eq!(1024 + 3, h.sub_bucket_for((unit << 21) + 3 * (unit << 11), 11));
}

#[test]
fn unit_magnitude_52_sub_bucket_magnitude_11_index_calculations() {
    // maximum unit magnitude for this precision
    let h = histo64(1_u64 << 52, u64::max_value(), 3);
    assert_eq!(2048, h.sub_bucket_count);
    assert_eq!(52, h.unit_magnitude);
    // sub_bucket_count = 2^11. With unit magnitude shift, it's 2^63. 1 more bucket to (almost)
    // reach 2^64.
    assert_eq!(2, h.bucket_count);
    assert_eq!(1, h.leading_zero_count_base);
    let unit = 1_u64 << 52;

    // below lowest value
    assert_eq!(0, h.bucket_for(3));
    assert_eq!(0, h.sub_bucket_for(3, 0));

    // first half of first bucket
    assert_eq!(0, h.bucket_for(3 * unit));
    assert_eq!(3, h.sub_bucket_for(3 * unit, 0));

    // second half of first bucket
    // sub_bucket_half_count's worth of units, plus 3 more
    assert_eq!(0, h.bucket_for(unit * (1024 + 3)));
    assert_eq!(1024 + 3, h.sub_bucket_for(unit * (1024 + 3), 0));

    // end of second half
    assert_eq!(0, h.bucket_for(unit * 1024 + 1023 * unit));
    assert_eq!(1024 + 1023, h.sub_bucket_for(unit * 1024 + 1023 * unit, 0));

    // second bucket (top half), bucket scale = unit << 1.
    // Middle of bucket is (sub_bucket_half_count = 2^10) of bucket scale, = unit << 11.
    // Add on 3 of bucket scale.
    assert_eq!(1, h.bucket_for((unit << 11) + 3 * (unit << 1)));
    assert_eq!(1024 + 3, h.sub_bucket_for((unit << 11) + 3 * (unit << 1), 1));

    // upper half of second bucket, last slot
    assert_eq!(1, h.bucket_for(u64::max_value()));
    assert_eq!(1024 + 1023, h.sub_bucket_for(u64::max_value(), 1));
}

#[test]
fn unit_magnitude_53_sub_bucket_magnitude_11_throws() {
    assert_eq!(CreationError::CannotRepresentSigFigBeyondLow,
        Histogram::<u64>::new_with_bounds(1_u64 << 53, 1_u64 << 63, 3).unwrap_err());
}

#[test]
fn unit_magnitude_55_sub_bucket_magnitude_8_ok() {
    let h = histo64(1_u64 << 55, 1_u64 << 63, 2);
    assert_eq!(256, h.sub_bucket_count);
    assert_eq!(55, h.unit_magnitude);
    // sub_bucket_count = 2^8. With unit magnitude shift, it's 2^63.
    assert_eq!(2, h.bucket_count);

    // below lowest value
    assert_eq!(0, h.bucket_for(3));
    assert_eq!(0, h.sub_bucket_for(3, 0));

    // upper half of second bucket, last slot
    assert_eq!(1, h.bucket_for(u64::max_value()));
    assert_eq!(128 + 127, h.sub_bucket_for(u64::max_value(), 1));
}

#[test]
fn unit_magnitude_62_sub_bucket_magnitude_1_ok() {
    let h = histo64(1_u64 << 62, 1_u64 << 63, 0);
    assert_eq!(2, h.sub_bucket_count);
    assert_eq!(62, h.unit_magnitude);
    // sub_bucket_count = 2^1. With unit magnitude shift, it's 2^63.
    assert_eq!(2, h.bucket_count);

    // below lowest value
    assert_eq!(0, h.bucket_for(3));
    assert_eq!(0, h.sub_bucket_for(3, 0));

    // upper half of second bucket, last slot
    assert_eq!(1, h.bucket_for(u64::max_value()));
    assert_eq!(1, h.sub_bucket_for(u64::max_value(), 1));
}
<commit_msg>Add tests for bucket and sub bucket indexing<commit_after>use super::super::{CreationError, Histogram};
use tests::helpers::histo64;

#[test]
fn unit_magnitude_0_index_calculations() {
    let h = histo64(1_u64, 1_u64 << 32, 3);
    assert_eq!(2048, h.sub_bucket_count);
    assert_eq!(0, h.unit_magnitude);
    // sub_bucket_count = 2^11, so 2^11 << 22 is > the max of 2^32 for 23 buckets total
    assert_eq!(23, h.bucket_count);

    // first half of first bucket
    assert_eq!(0, h.bucket_for(3));
    assert_eq!(3, h.sub_bucket_for(3, 0));

    // second half of first bucket
    assert_eq!(0, h.bucket_for(1024 + 3));
    assert_eq!(1024 + 3, h.sub_bucket_for(1024 + 3, 0));

    // second bucket (top half)
    assert_eq!(1, h.bucket_for(2048 + 3 * 2));
    // counting by 2s, starting at halfway through the bucket
    assert_eq!(1024 + 3, h.sub_bucket_for(2048 + 3 * 2, 1));

    // third bucket (top half)
    assert_eq!(2, h.bucket_for((2048 << 1) + 3 * 4));
    // counting by 4s, starting at halfway through the bucket
    assert_eq!(1024 + 3, h.sub_bucket_for((2048 << 1) + 3 * 4, 2));

    // past last bucket -- not near u64::max_value(), so should still calculate ok.
    assert_eq!(23, h.bucket_for((2048_u64 << 22) + 3 * (1 << 23)));
    assert_eq!(1024 + 3, h.sub_bucket_for((2048_u64 << 22) + 3 * (1 << 23), 23));
}

#[test]
fn unit_magnitude_4_index_calculations() {
    let h = histo64(1_u64 << 12, 1_u64 << 32, 3);
    assert_eq!(2048, h.sub_bucket_count);
    assert_eq!(12, h.unit_magnitude);
    // sub_bucket_count = 2^11. With unit magnitude shift, it's 2^23. 2^23 << 10 is > the max of
    // 2^32 for 11 buckets total
    assert_eq!(11, h.bucket_count);
    let unit = 1_u64 << 12;

    // below lowest value
    assert_eq!(0, h.bucket_for(3));
    assert_eq!(0, h.sub_bucket_for(3, 0));

    // first half of first bucket
    assert_eq!(0, h.bucket_for(3 * unit));
    assert_eq!(3, h.sub_bucket_for(3 * unit, 0));

    // second half of first bucket
    // sub_bucket_half_count's worth of units, plus 3 more
    assert_eq!(0, h.bucket_for(unit * (1024 + 3)));
    assert_eq!(1024 + 3, h.sub_bucket_for(unit * (1024 + 3), 0));

    // second bucket (top half), bucket scale = unit << 1.
    // Middle of bucket is (sub_bucket_half_count = 2^10) of bucket scale, = unit << 11.
    // Add on 3 of bucket scale.
    assert_eq!(1, h.bucket_for((unit << 11) + 3 * (unit << 1)));
    assert_eq!(1024 + 3, h.sub_bucket_for((unit << 11) + 3 * (unit << 1), 1));

    // third bucket (top half), bucket scale = unit << 2.
    // Middle of bucket is (sub_bucket_half_count = 2^10) of bucket scale, = unit << 12.
    // Add on 3 of bucket scale.
    assert_eq!(2, h.bucket_for((unit << 12) + 3 * (unit << 2)));
    assert_eq!(1024 + 3, h.sub_bucket_for((unit << 12) + 3 * (unit << 2), 2));

    // past last bucket -- not near u64::max_value(), so should still calculate ok.
    assert_eq!(11, h.bucket_for((unit << 21) + 3 * (unit << 11)));
    assert_eq!(1024 + 3, h.sub_bucket_for((unit << 21) + 3 * (unit << 11), 11));
}

#[test]
fn unit_magnitude_52_sub_bucket_magnitude_11_index_calculations() {
    // maximum unit magnitude for this precision
    let h = histo64(1_u64 << 52, u64::max_value(), 3);
    assert_eq!(2048, h.sub_bucket_count);
    assert_eq!(52, h.unit_magnitude);
    // sub_bucket_count = 2^11. With unit magnitude shift, it's 2^63. 1 more bucket to (almost)
    // reach 2^64.
    assert_eq!(2, h.bucket_count);
    assert_eq!(1, h.leading_zero_count_base);
    let unit = 1_u64 << 52;

    // below lowest value
    assert_eq!(0, h.bucket_for(3));
    assert_eq!(0, h.sub_bucket_for(3, 0));

    // first half of first bucket
    assert_eq!(0, h.bucket_for(3 * unit));
    assert_eq!(3, h.sub_bucket_for(3 * unit, 0));

    // second half of first bucket
    // sub_bucket_half_count's worth of units, plus 3 more
    assert_eq!(0, h.bucket_for(unit * (1024 + 3)));
    assert_eq!(1024 + 3, h.sub_bucket_for(unit * (1024 + 3), 0));

    // end of second half
    assert_eq!(0, h.bucket_for(unit * 1024 + 1023 * unit));
    assert_eq!(1024 + 1023, h.sub_bucket_for(unit * 1024 + 1023 * unit, 0));

    // second bucket (top half), bucket scale = unit << 1.
    // Middle of bucket is (sub_bucket_half_count = 2^10) of bucket scale, = unit << 11.
    // Add on 3 of bucket scale.
    assert_eq!(1, h.bucket_for((unit << 11) + 3 * (unit << 1)));
    assert_eq!(1024 + 3, h.sub_bucket_for((unit << 11) + 3 * (unit << 1), 1));

    // upper half of second bucket, last slot
    assert_eq!(1, h.bucket_for(u64::max_value()));
    assert_eq!(1024 + 1023, h.sub_bucket_for(u64::max_value(), 1));
}

#[test]
fn unit_magnitude_53_sub_bucket_magnitude_11_throws() {
    assert_eq!(CreationError::CannotRepresentSigFigBeyondLow,
        Histogram::<u64>::new_with_bounds(1_u64 << 53, 1_u64 << 63, 3).unwrap_err());
}

#[test]
fn unit_magnitude_55_sub_bucket_magnitude_8_ok() {
    let h = histo64(1_u64 << 55, 1_u64 << 63, 2);
    assert_eq!(256, h.sub_bucket_count);
    assert_eq!(55, h.unit_magnitude);
    // sub_bucket_count = 2^8. With unit magnitude shift, it's 2^63.
    assert_eq!(2, h.bucket_count);

    // below lowest value
    assert_eq!(0, h.bucket_for(3));
    assert_eq!(0, h.sub_bucket_for(3, 0));

    // upper half of second bucket, last slot
    assert_eq!(1, h.bucket_for(u64::max_value()));
    assert_eq!(128 + 127, h.sub_bucket_for(u64::max_value(), 1));
}

#[test]
fn unit_magnitude_62_sub_bucket_magnitude_1_ok() {
    let h = histo64(1_u64 << 62, 1_u64 << 63, 0);
    assert_eq!(2, h.sub_bucket_count);
    assert_eq!(62, h.unit_magnitude);
    // sub_bucket_count = 2^1. With unit magnitude shift, it's 2^63.
    assert_eq!(2, h.bucket_count);

    // below lowest value
    assert_eq!(0, h.bucket_for(3));
    assert_eq!(0, h.sub_bucket_for(3, 0));

    // upper half of second bucket, last slot
    assert_eq!(1, h.bucket_for(u64::max_value()));
    assert_eq!(1, h.sub_bucket_for(u64::max_value(), 1));
}

#[test]
fn bucket_for_smallest_value_in_first_bucket() {
    let h = histo64(1, 100_000, 3);
    assert_eq!(0, h.bucket_for(0))
}

#[test]
fn bucket_for_biggest_value_in_first_bucket() {
    let h = histo64(1, 100_000, 3);
    // sub bucket size 2048, and first bucket uses all 2048 slots
    assert_eq!(0, h.bucket_for(2047))
}

#[test]
fn bucket_for_smallest_value_in_second_bucket() {
    let h = histo64(1, 100_000, 3);
    assert_eq!(1, h.bucket_for(2048))
}

#[test]
fn bucket_for_biggest_value_in_second_bucket() {
    let h = histo64(1, 100_000, 3);
    // second value uses only 1024 slots, but scales by 2
    assert_eq!(1, h.bucket_for(4095))
}

#[test]
fn bucket_for_smallest_value_in_third_bucket() {
    let h = histo64(1, 100_000, 3);
    assert_eq!(2, h.bucket_for(4096))
}

#[test]
fn bucket_for_smallest_value_in_last_bucket() {
    let h = histo64(1, 100_000, 3);

    // 7 buckets total
    assert_eq!(6, h.bucket_for(65536))
}

#[test]
fn bucket_for_value_below_smallest_clamps_to_zero() {
    let h = histo64(1024, 100_000, 3);

    // masking clamps bucket index to 0
    assert_eq!(0, h.bucket_for(0));
    assert_eq!(0, h.bucket_for(1));
    assert_eq!(0, h.bucket_for(1023));
    assert_eq!(0, h.bucket_for(1024))
}

#[test]
fn bucket_for_value_above_biggest_isnt_clamped_at_max_bucket() {
    let h = histo64(1, 100_000, 3);

    assert_eq!(6, h.bucket_for(100_000));
    // 2048 * 2^26 = 137,438,953,472
    assert_eq!(26, h.bucket_for(100_000_000_000));
}

#[test]
fn sub_bucket_for_zero_value_in_first_bucket() {
    let h = histo64(1, 100_000, 3);
    // below min distinguishable value, but still gets bucketed into 0
    let value = 0;
    assert_eq!(0, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_smallest_distinguishable_value_in_first_bucket() {
    let h = histo64(1, 100_000, 3);
    let value = 1;
    assert_eq!(1, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_zero_value_in_first_bucket_unit_magnitude_2() {
    let h = histo64(4, 100_000, 3);
    let value = 0;
    assert_eq!(2, h.unit_magnitude);
    assert_eq!(0, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_smaller_than_distinguishable_value_in_first_bucket_unit_magnitude_2() {
    let h = histo64(4, 100_000, 3);
    let value = 3;
    assert_eq!(2, h.unit_magnitude);
    assert_eq!(0, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_smallest_distinguishable_value_in_first_bucket_unit_magnitude_2() {
    let h = histo64(4, 100_000, 3);
    let value = 4;
    assert_eq!(2, h.unit_magnitude);
    assert_eq!(1, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_largest_value_in_first_bucket_unit_magnitude_2() {
    let h = histo64(4, 100_000, 3);
    let value = 2048 * 4 - 1;
    assert_eq!(2, h.unit_magnitude);
    assert_eq!(2047, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_smallest_value_in_second_bucket_unit_magnitude_2() {
    let h = histo64(4, 100_000, 3);
    let value = 2048 * 4;
    assert_eq!(2, h.unit_magnitude);
    assert_eq!(1024, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_largest_value_in_first_bucket() {
    let h = histo64(1, 100_000, 3);
    let value = 2047;
    assert_eq!(2047, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_smallest_value_in_second_bucket() {
    let h = histo64(1, 100_000, 3);
    let value = 2048;

    // at midpoint of bucket, which is the first position actually used in second bucket
    assert_eq!(1024, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_biggest_value_in_second_bucket() {
    let h = histo64(1, 100_000, 3);
    let value = 4095;

    // at endpoint of bucket, which is the last position actually used in second bucket
    assert_eq!(2047, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_smallest_value_in_third_bucket() {
    let h = histo64(1, 100_000, 3);
    let value = 4096;

    assert_eq!(1024, h.sub_bucket_for(value, h.bucket_for(value)))
}

#[test]
fn sub_bucket_for_value_below_smallest_clamps_to_zero() {
    let h = histo64(1024, 100_000, 3);

    assert_eq!(0, h.sub_bucket_for(0, 0));
    assert_eq!(0, h.sub_bucket_for(1, 0));
    assert_eq!(0, h.sub_bucket_for(1023, 0));
    assert_eq!(1, h.sub_bucket_for(1024, 0))
}

#[test]
fn sub_bucket_for_value_above_biggest_still_works() {
    let h = histo64(1, 1024 * 1024, 3);

    // normal case:
    // in bucket index 6, scales by 2^6 = 64, start is at 65536.
    // 100_000 - 65536 = 34_464. 34464 / 64 = 538.5. +1024 = 1562
    assert_eq!(1562, h.sub_bucket_for(100_000, h.bucket_for(100_000)));

    // still in sub bucket count but nonsensical
    // In bucket 26, effective start is 1024 * 2^26 = 68,719,476,736.
    // 100b - start = 31,280,523,264. That / 2^26 = 466.1.
    assert_eq!(466 + 1024, h.sub_bucket_for(100_000_000_000, h.bucket_for(100_000_000_000)));
}

#[test]
fn index_for_first_bucket_first_entry() {
    let h = histo64(1, 100_000, 3);
    assert_eq!(0, h.index_for(0));
}

#[test]
fn index_for_first_bucket_first_distinguishable_entry() {
    let h = histo64(1, 100_000, 3);
    assert_eq!(1, h.index_for(1));
}

#[test]
fn index_for_first_bucket_last_entry() {
    let h = histo64(1, 100_000, 3);
    assert_eq!(2047, h.index_for(2047));
}

#[test]
fn index_for_second_bucket_last_entry() {
    let h = histo64(1, 100_000, 3);
    assert_eq!(2048 + 1023, h.index_for(2048 + 2047));
}

#[test]
fn index_for_second_bucket_last_entry_indistinguishable() {
    let h = histo64(1, 100_000, 3);
    assert_eq!(2048 + 1023, h.index_for(2048 + 2046));
}

#[test]
fn index_for_second_bucket_first_entry() {
    let h = histo64(1, 100_000, 3);
    assert_eq!(2048, h.index_for(2048));
}

#[test]
fn index_for_below_smallest() {
    let h = histo64(1024, 100_000, 3);

    assert_eq!(0, h.index_for(512));
}

#[test]
fn index_for_way_past_largest_value_exceeds_length() {
    let h = histo64(1, 100_000, 3);

    // 7 * 1024 + 1 more 1024
    assert_eq!(8 * 1024, h.counts.len());

    // 2^39 = 1024 * 2^29, so this should be the start of the 30th bucket.
    // Start index is (bucket index + 1) * 1024.
    assert_eq!(1024 * (30 + 1), h.index_for(1 << 40));
}
<|endoftext|>


<commit_before>use std::env;
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::{Command, Stdio};

fn main() {
    if env::var("RUSTC_WRAPPER").map_or(false, |wrapper| wrapper.contains("sccache")) {
        eprintln!(
            "\x1b[1;93m=== Warning: Unsetting RUSTC_WRAPPER to prevent interference with sccache ===\x1b[0m"
        );
        env::remove_var("RUSTC_WRAPPER");
    }

    let sysroot = PathBuf::from(env::current_exe().unwrap().parent().unwrap());

    env::set_var("RUSTC", sysroot.join("bin/cg_clif".to_string() + env::consts::EXE_SUFFIX));

    let mut rustdoc_flags = env::var("RUSTDOCFLAGS").unwrap_or(String::new());
    rustdoc_flags.push_str(" -Cpanic=abort -Zpanic-abort-tests -Zcodegen-backend=");
    rustdoc_flags.push_str(
        sysroot
            .join(if cfg!(windows) { "bin" } else { "lib" })
            .join(
                env::consts::DLL_PREFIX.to_string()
                    + "rustc_codegen_cranelift"
                    + env::consts::DLL_SUFFIX,
            )
            .to_str()
            .unwrap(),
    );
    rustdoc_flags.push_str(" --sysroot ");
    rustdoc_flags.push_str(sysroot.to_str().unwrap());
    env::set_var("RUSTDOCFLAGS", rustdoc_flags);

    let default_sysroot = Command::new("rustc")
        .stderr(Stdio::inherit())
        .args(&["--print", "sysroot"])
        .output()
        .unwrap()
        .stdout;
    let default_sysroot = std::str::from_utf8(&default_sysroot).unwrap().trim();

    let extra_ld_lib_path =
        default_sysroot.to_string() + ":" + sysroot.join("lib").to_str().unwrap();
    if cfg!(target_os = "macos") {
        env::set_var(
            "DYLD_LIBRARY_PATH",
            env::var("DYLD_LIBRARY_PATH").unwrap_or(String::new()) + ":" + &extra_ld_lib_path,
        );
    } else if cfg!(unix) {
        env::set_var(
            "LD_LIBRARY_PATH",
            env::var("LD_LIBRARY_PATH").unwrap_or(String::new()) + ":" + &extra_ld_lib_path,
        );
    }

    // Ensure that the right toolchain is used
    env::set_var("RUSTUP_TOOLCHAIN", env!("RUSTUP_TOOLCHAIN"));

    let args: Vec<_> = match env::args().nth(1).as_deref() {
        Some("jit") => {
            env::set_var(
                "RUSTFLAGS",
                env::var("RUSTFLAGS").unwrap_or(String::new()) + " -Cprefer-dynamic",
            );
            std::array::IntoIter::new(["rustc".to_string()])
                .chain(env::args().skip(2))
                .chain(["--".to_string(), "-Cllvm-args=mode=jit".to_string()])
                .collect()
        }
        Some("lazy-jit") => {
            env::set_var(
                "RUSTFLAGS",
                env::var("RUSTFLAGS").unwrap_or(String::new()) + " -Cprefer-dynamic",
            );
            std::array::IntoIter::new(["rustc".to_string()])
                .chain(env::args().skip(2))
                .chain(["--".to_string(), "-Cllvm-args=mode=jit-lazy".to_string()])
                .collect()
        }
        _ => env::args().skip(1).collect(),
    };

    Command::new("cargo").args(args).exec();
}
<commit_msg>Fix compiling cargo.rs on windows<commit_after>use std::env;
#[cfg(unix)]
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::{Command, Stdio};

fn main() {
    if env::var("RUSTC_WRAPPER").map_or(false, |wrapper| wrapper.contains("sccache")) {
        eprintln!(
            "\x1b[1;93m=== Warning: Unsetting RUSTC_WRAPPER to prevent interference with sccache ===\x1b[0m"
        );
        env::remove_var("RUSTC_WRAPPER");
    }

    let sysroot = PathBuf::from(env::current_exe().unwrap().parent().unwrap());

    env::set_var("RUSTC", sysroot.join("bin/cg_clif".to_string() + env::consts::EXE_SUFFIX));

    let mut rustdoc_flags = env::var("RUSTDOCFLAGS").unwrap_or(String::new());
    rustdoc_flags.push_str(" -Cpanic=abort -Zpanic-abort-tests -Zcodegen-backend=");
    rustdoc_flags.push_str(
        sysroot
            .join(if cfg!(windows) { "bin" } else { "lib" })
            .join(
                env::consts::DLL_PREFIX.to_string()
                    + "rustc_codegen_cranelift"
                    + env::consts::DLL_SUFFIX,
            )
            .to_str()
            .unwrap(),
    );
    rustdoc_flags.push_str(" --sysroot ");
    rustdoc_flags.push_str(sysroot.to_str().unwrap());
    env::set_var("RUSTDOCFLAGS", rustdoc_flags);

    let default_sysroot = Command::new("rustc")
        .stderr(Stdio::inherit())
        .args(&["--print", "sysroot"])
        .output()
        .unwrap()
        .stdout;
    let default_sysroot = std::str::from_utf8(&default_sysroot).unwrap().trim();

    let extra_ld_lib_path =
        default_sysroot.to_string() + ":" + sysroot.join("lib").to_str().unwrap();
    if cfg!(target_os = "macos") {
        env::set_var(
            "DYLD_LIBRARY_PATH",
            env::var("DYLD_LIBRARY_PATH").unwrap_or(String::new()) + ":" + &extra_ld_lib_path,
        );
    } else if cfg!(unix) {
        env::set_var(
            "LD_LIBRARY_PATH",
            env::var("LD_LIBRARY_PATH").unwrap_or(String::new()) + ":" + &extra_ld_lib_path,
        );
    }

    // Ensure that the right toolchain is used
    env::set_var("RUSTUP_TOOLCHAIN", env!("RUSTUP_TOOLCHAIN"));

    let args: Vec<_> = match env::args().nth(1).as_deref() {
        Some("jit") => {
            env::set_var(
                "RUSTFLAGS",
                env::var("RUSTFLAGS").unwrap_or(String::new()) + " -Cprefer-dynamic",
            );
            std::array::IntoIter::new(["rustc".to_string()])
                .chain(env::args().skip(2))
                .chain(["--".to_string(), "-Cllvm-args=mode=jit".to_string()])
                .collect()
        }
        Some("lazy-jit") => {
            env::set_var(
                "RUSTFLAGS",
                env::var("RUSTFLAGS").unwrap_or(String::new()) + " -Cprefer-dynamic",
            );
            std::array::IntoIter::new(["rustc".to_string()])
                .chain(env::args().skip(2))
                .chain(["--".to_string(), "-Cllvm-args=mode=jit-lazy".to_string()])
                .collect()
        }
        _ => env::args().skip(1).collect(),
    };

    #[cfg(unix)]
    Command::new("cargo").args(args).exec();

    #[cfg(not(unix))]
    std::process::exit(
        Command::new("cargo").args(args).spawn().unwrap().wait().unwrap().code().unwrap_or(1),
    );
}
<|endoftext|>


<commit_before>#![crate_id = "rboy"]
#![license = "MIT"]

#![feature(phase)]
#[phase(plugin, link)] extern crate log;

extern crate native;
extern crate getopts;
extern crate time;
extern crate sdl;
#[cfg(test)]
extern crate test;

use cpu::CPU;
use std::sync::{Arc,RWLock};
use std::comm::{DuplexStream,Disconnected,Empty};
use std::task::TaskBuilder;
use native::NativeTaskBuilder;

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;
mod gbmode;
mod util;

static SCALE: uint = 2;
static EXITCODE_INCORRECTOPTIONS: int = 1;
static EXITCODE_CPULOADFAILS: int = 2;

fn main() {
	let args = std::os::args();
	let opts = [ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f); return }
	};

	let filename = if !matches.free.is_empty() {
		matches.free.get(0).clone()
	} else {
		println!("{}", getopts::usage(args.get(0).clone().append(" <filename>").as_slice(), opts));
		std::os::set_exit_status(EXITCODE_INCORRECTOPTIONS);
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE as int, 144*SCALE as int, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = std::comm::duplex();
	let rawscreen = [0x00u8,.. 160*144*3];
	let arc = Arc::new(RWLock::new(rawscreen));
	let arc2 = arc.clone();

	TaskBuilder::new().native().spawn(proc() cpuloop(&cpustream, arc2, filename.as_slice(), &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			Err(Disconnected) => { break 'main },
			Ok(_) => recalculate_screen(&screen, &arc),
			Err(Empty) => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdlstream.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdlstream.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &Arc<RWLock<[u8,.. 160*144*3]>>) {
	let data = arc.read();
	for y in range(0u, 144) {
		for x in range(0u, 160) {
			screen.fill_rect(
				Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
				sdl::video::RGB(data[y*160*3 + x*3 + 0],
				                data[y*160*3 + x*3 + 1],
				                data[y*160*3 + x*3 + 2])
			);
		}
	}
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: Arc<RWLock<[u8,.. 160*144*3]>>, filename: &str, matches: &getopts::Matches) {
	let opt_c = match matches.opt_present("classic") {
		true => CPU::new(filename),
		false => CPU::new_cgb(filename),
	};
	let mut c = match opt_c
	{
		Some(cpu) => { cpu },
		None => { error!("Could not get a valid gameboy"); std::os::set_exit_status(EXITCODE_CPULOADFAILS); return; },
	};
	c.mmu.serial.tostdout = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);
	let mut limit_speed = true;

	let waitticks = (4194.304f32 * 4.0) as uint;

	let mut ticks = 0;
	'cpuloop: loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				let mut data = arc.write();
				for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				if channel.send_opt(0).is_err() { break 'cpuloop };
			}
		}
		ticks -= waitticks;
		if limit_speed { periodic.recv(); }

		match channel.try_recv() {
			Ok(event) => match event {
				KeyUp(key) => c.mmu.keypad.keyup(key),
				KeyDown(key) => c.mmu.keypad.keydown(key),
				SpeedUp => limit_speed = false,
				SlowDown => limit_speed = true,
			},
			Err(Empty) => {},
			Err(Disconnected) => { break },
		};
	}
}
<commit_msg>Switch from deprecated duplex() to channel()<commit_after>#![crate_id = "rboy"]
#![license = "MIT"]

#![feature(phase)]
#[phase(plugin, link)] extern crate log;

extern crate native;
extern crate getopts;
extern crate time;
extern crate sdl;
#[cfg(test)]
extern crate test;

use cpu::CPU;
use std::sync::{Arc,RWLock};
use std::comm::{Sender,Receiver,Disconnected,Empty};
use std::task::TaskBuilder;
use native::NativeTaskBuilder;

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;
mod gbmode;
mod util;

static SCALE: uint = 2;
static EXITCODE_INCORRECTOPTIONS: int = 1;
static EXITCODE_CPULOADFAILS: int = 2;

fn main() {
	let args = std::os::args();
	let opts = [ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f); return }
	};

	let filename = if !matches.free.is_empty() {
		matches.free.get(0).clone()
	} else {
		println!("{}", getopts::usage(args.get(0).clone().append(" <filename>").as_slice(), opts));
		std::os::set_exit_status(EXITCODE_INCORRECTOPTIONS);
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE as int, 144*SCALE as int, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdl_tx, cpu_rx) = std::comm::channel();
	let (cpu_tx, sdl_rx) = std::comm::channel();
	let rawscreen = [0x00u8,.. 160*144*3];
	let arc = Arc::new(RWLock::new(rawscreen));
	let arc2 = arc.clone();

	TaskBuilder::new().native().spawn(proc() cpuloop(&cpu_tx, &cpu_rx, arc2, filename.as_slice(), &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdl_rx.try_recv() {
			Err(Disconnected) => { break 'main },
			Ok(_) => recalculate_screen(&screen, &arc),
			Err(Empty) => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdl_tx.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdl_tx.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdl_tx.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdl_tx.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &Arc<RWLock<[u8,.. 160*144*3]>>) {
	let data = arc.read();
	for y in range(0u, 144) {
		for x in range(0u, 160) {
			screen.fill_rect(
				Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
				sdl::video::RGB(data[y*160*3 + x*3 + 0],
				                data[y*160*3 + x*3 + 1],
				                data[y*160*3 + x*3 + 2])
			);
		}
	}
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(cpu_tx: &Sender<uint>, cpu_rx: &Receiver<GBEvent>, arc: Arc<RWLock<[u8,.. 160*144*3]>>, filename: &str, matches: &getopts::Matches) {
	let opt_c = match matches.opt_present("classic") {
		true => CPU::new(filename),
		false => CPU::new_cgb(filename),
	};
	let mut c = match opt_c
	{
		Some(cpu) => { cpu },
		None => { error!("Could not get a valid gameboy"); std::os::set_exit_status(EXITCODE_CPULOADFAILS); return; },
	};
	c.mmu.serial.tostdout = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);
	let mut limit_speed = true;

	let waitticks = (4194.304f32 * 4.0) as uint;

	let mut ticks = 0;
	'cpuloop: loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				let mut data = arc.write();
				for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				if cpu_tx.send_opt(0).is_err() { break 'cpuloop };
			}
		}
		ticks -= waitticks;
		if limit_speed { periodic.recv(); }

		match cpu_rx.try_recv() {
			Ok(event) => match event {
				KeyUp(key) => c.mmu.keypad.keyup(key),
				KeyDown(key) => c.mmu.keypad.keydown(key),
				SpeedUp => limit_speed = false,
				SlowDown => limit_speed = true,
			},
			Err(Empty) => {},
			Err(Disconnected) => { break },
		};
	}
}
<|endoftext|>


<commit_before>use std::ops::Add;
use std::num::{Float, FromPrimitive, ToPrimitive, FromStrRadix};
use std::cmp::min;
use std::fmt;
use std::fmt::Writer;
use std::{f64, isize};
use std::cmp::Ordering;

use num::bigint::{BigInt, ToBigInt};
use num::rational::{Ratio, BigRational};
use num::{Signed, Zero, Integer, CheckedAdd};

#[derive(Debug)]
pub enum Real {
    Fixnum(isize),
    Integer(BigInt),
    Rational(BigRational),
    Flonum(f64)
}

impl fmt::Display for Real {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Real::Fixnum(n) => write!(f, "{}", n),
            &Real::Integer(ref n) => write!(f, "{}", n),
            &Real::Rational(ref n) => write!(f, "{}", n),
            &Real::Flonum(n) => write!(f, "{}", n)
        }
    }
}

#[inline]
pub fn fix2int(n: isize) -> BigInt {
    n.to_bigint().unwrap()
}

#[inline]
pub fn int2rat(n: &BigInt) -> BigRational {
    Ratio::from_integer(n.clone())
}

#[inline]
pub fn fix2rat(n: isize) -> BigRational {
    int2rat(&fix2int(n))
}

#[inline]
pub fn fix2flo(n: isize) -> f64 {
    FromPrimitive::from_int(n).unwrap()
}

pub fn int2flo(n: &BigInt) -> f64 {
    if n.is_zero() {
        return 0f64;
    }

    let neg = *n < Zero::zero();
    let repr = {
        let mut digits = Vec::new();
        let mut m = n.abs();
        let base: BigInt = FromPrimitive::from_u64(1 << 32).unwrap();
        while m >= base {
            let (q, r) = m.div_mod_floor(&base);
            digits.push(r.to_uint().unwrap());
            m = q;
        }
        if !m.is_zero() {
            digits.push(m.to_uint().unwrap());
        }
        let mut s = String::new();
        for &d in digits.iter().rev() {
            write!(&mut s, "{}", fmt::radix(d, 2)).unwrap();
        }
        s
    };
    let mantissa_repr = &repr[0 .. min(f64::MANTISSA_DIGITS, repr.len())];
    let u_mantissa:i64 = FromStrRadix::from_str_radix(mantissa_repr,
                                                        2).unwrap();
    let i_mantissa = if neg {
        -u_mantissa
    } else {
        u_mantissa
    };
    let exp = (repr.len() - mantissa_repr.len()) as isize;
    let m:f64 = i_mantissa.to_f64().unwrap();
    return Float::ldexp(m, exp);
}

pub fn rat2flo(r: &BigRational) -> f64 {
    int2flo(r.numer()) / int2flo(r.denom())
}

pub fn reduce(r: Real) -> Real {
    match r {
        Real::Fixnum(_) =>
            r,
        Real::Integer(n) => {
            let max_fixnum: BigInt = FromPrimitive::from_int(isize::MAX).unwrap();
            let min_fixnum: BigInt = FromPrimitive::from_int(isize::MIN).unwrap();

            if min_fixnum <= n && n <= max_fixnum {
                Real::Fixnum(n.to_int().unwrap())
            } else {
                Real::Integer(n)
            }
        },
        Real::Rational(n) =>
            if n.is_integer() {
                reduce(Real::Integer(n.to_integer()))
            } else {
                Real::Rational(n)
            },
        Real::Flonum(_) =>
            r
    }
}

fn coerce_arith<Fix, Big, Rat, Flo>(lhs: &Real, rhs: &Real,
                                    fix_op: Fix, big_op: Big,
                                    rat_op: Rat, flo_op: Flo)
        -> Real
    where Fix: Fn(isize, isize) -> Option<isize>,
          Big: Fn(&BigInt, &BigInt) -> BigInt,
          Rat: Fn(&BigRational, &BigRational) -> BigRational,
          Flo: Fn(f64, f64) -> f64
{
    coerce(lhs, rhs,
           |x, y| match fix_op(x, y) {
               Some(n) => Real::Fixnum(n),
               None => reduce(Real::Integer(big_op(&fix2int(x), &fix2int(y))))
           },
           |x, y| reduce(Real::Integer(big_op(x, y))),
           |x, y| reduce(Real::Rational(rat_op(x, y))),
           |x, y| Real::Flonum(flo_op(x, y))
    )
}

fn coerce<Fix, Big, Rat, Flo, T>(lhs: &Real, rhs: &Real,
                                 fix_op: Fix, big_op: Big,
                                 rat_op: Rat, flo_op: Flo)
        -> T
    where Fix: Fn(isize, isize) -> T,
          Big: Fn(&BigInt, &BigInt) -> T,
          Rat: Fn(&BigRational, &BigRational) -> T,
          Flo: Fn(f64, f64) -> T
{
    match (lhs, rhs) {
        (&Real::Fixnum(l), &Real::Fixnum(r)) =>
            fix_op(l, r),
        (&Real::Fixnum(l), &Real::Integer(ref r)) =>
            big_op(&fix2int(l), r),
        (&Real::Fixnum(l), &Real::Rational(ref r)) =>
            rat_op(&fix2rat(l), r),
        (&Real::Fixnum(l), &Real::Flonum(r)) =>
            flo_op(fix2flo(l), r),
        (&Real::Integer(ref l), &Real::Fixnum(r)) =>
            big_op(l, &fix2int(r)),
        (&Real::Integer(ref l), &Real::Integer(ref r)) =>
            big_op(l, r),
        (&Real::Integer(ref l), &Real::Rational(ref r)) =>
            rat_op(&int2rat(l), r),
        (&Real::Integer(ref l), &Real::Flonum(r)) =>
            flo_op(int2flo(l), r),
        (&Real::Rational(ref l), &Real::Fixnum(r)) =>
            rat_op(l, &fix2rat(r)),
        (&Real::Rational(ref l), &Real::Integer(ref r)) =>
            rat_op(l, &int2rat(r)),
        (&Real::Rational(ref l), &Real::Rational(ref r)) =>
            rat_op(l, r),
        (&Real::Rational(ref l), &Real::Flonum(r)) =>
            flo_op(rat2flo(l), r),
        (&Real::Flonum(l), &Real::Fixnum(r)) =>
            flo_op(l, fix2flo(r)),
        (&Real::Flonum(l), &Real::Integer(ref r)) =>
            flo_op(l, int2flo(r)),
        (&Real::Flonum(l), &Real::Rational(ref r)) =>
            flo_op(l, rat2flo(r)),
        (&Real::Flonum(l), &Real::Flonum(r)) =>
            flo_op(l, r)
    }
}

macro_rules! impl_arith {
    ($tr:ident, $op:ident, $checked_op:ident) => {
        impl $tr<Real> for Real {
            type Output = Real;

            fn $op(self, other: Real) -> Real {
                coerce_arith(&self, &other,
                             |x, y| x.$checked_op(&y),
                             |x, y| x.$op(y),
                             |x, y| x.$op(y),
                             |x, y| x.$op(&y)
                )
            }
        }

        impl<'a, 'b> $tr<&'a Real> for &'b Real {
            type Output = Real;

            fn $op(self, other: &Real) -> Real {
                coerce_arith(self, other,
                             |x, y| x.$checked_op(&y),
                             |x, y| x.$op(y),
                             |x, y| x.$op(y),
                             |x, y| x.$op(&y)
                )
            }
        }
    }
}

impl_arith!(Add, add, checked_add);

impl PartialEq for Real {
    fn eq(&self, other: &Real) -> bool {
        coerce(self, other,
               |x, y| x == y,
               |x, y| x == y,
               |x, y| x == y,
               |x, y| x == y
        )
    }
}

impl PartialOrd for Real {
    fn partial_cmp(&self, other: &Real) -> Option<Ordering> {
        coerce(self, other,
                    |x, y| x.partial_cmp(&y),
                    |x, y| x.partial_cmp(y),
                    |x, y| x.partial_cmp(y),
                    |x, y| x.partial_cmp(&y)
        )
    }
}

#[cfg(test)]
mod test {
    use super::{Real, int2flo};
    use std::num::FromPrimitive;

    #[test]
    fn test_add() {
        assert_eq!(Real::Fixnum(3), Real::Fixnum(1) + Real::Fixnum(2));
    }

    #[test]
    fn test_reduce() {
        let x = Real::Integer(FromPrimitive::from_int(1).unwrap());
        let y = Real::Integer(FromPrimitive::from_int(2).unwrap());
        assert_eq!(Real::Fixnum(3), x+y);
    }

    #[test]
    fn test_int2flo() {
        let n = FromPrimitive::from_int(3).unwrap();
        assert_eq!(3.0, int2flo(&n));
    }
}
<commit_msg>impl Sub, Mul and Div for Real<commit_after>use std::ops::{Add, Sub, Mul, Div};
use std::num::{Float, FromPrimitive, ToPrimitive, FromStrRadix};
use std::cmp::min;
use std::fmt;
use std::fmt::Writer;
use std::{f64, isize};
use std::cmp::Ordering;

use num::bigint::{BigInt, ToBigInt};
use num::rational::{Ratio, BigRational};
use num::{Signed, Zero, Integer, CheckedAdd, CheckedSub, CheckedMul};

#[derive(Debug)]
pub enum Real {
    Fixnum(isize),
    Integer(BigInt),
    Rational(BigRational),
    Flonum(f64)
}

impl fmt::Display for Real {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Real::Fixnum(n) => write!(f, "{}", n),
            &Real::Integer(ref n) => write!(f, "{}", n),
            &Real::Rational(ref n) => write!(f, "{}", n),
            &Real::Flonum(n) => write!(f, "{}", n)
        }
    }
}

#[inline]
pub fn fix2int(n: isize) -> BigInt {
    n.to_bigint().unwrap()
}

#[inline]
pub fn int2rat(n: &BigInt) -> BigRational {
    Ratio::from_integer(n.clone())
}

#[inline]
pub fn fix2rat(n: isize) -> BigRational {
    int2rat(&fix2int(n))
}

#[inline]
pub fn fix2flo(n: isize) -> f64 {
    FromPrimitive::from_int(n).unwrap()
}

pub fn int2flo(n: &BigInt) -> f64 {
    if n.is_zero() {
        return 0f64;
    }

    let neg = *n < Zero::zero();
    let repr = {
        let mut digits = Vec::new();
        let mut m = n.abs();
        let base: BigInt = FromPrimitive::from_u64(1 << 32).unwrap();
        while m >= base {
            let (q, r) = m.div_mod_floor(&base);
            digits.push(r.to_uint().unwrap());
            m = q;
        }
        if !m.is_zero() {
            digits.push(m.to_uint().unwrap());
        }
        let mut s = String::new();
        for &d in digits.iter().rev() {
            write!(&mut s, "{}", fmt::radix(d, 2)).unwrap();
        }
        s
    };
    let mantissa_repr = &repr[0 .. min(f64::MANTISSA_DIGITS, repr.len())];
    let u_mantissa:i64 = FromStrRadix::from_str_radix(mantissa_repr,
                                                        2).unwrap();
    let i_mantissa = if neg {
        -u_mantissa
    } else {
        u_mantissa
    };
    let exp = (repr.len() - mantissa_repr.len()) as isize;
    let m:f64 = i_mantissa.to_f64().unwrap();
    return Float::ldexp(m, exp);
}

pub fn rat2flo(r: &BigRational) -> f64 {
    int2flo(r.numer()) / int2flo(r.denom())
}

pub fn reduce(r: Real) -> Real {
    match r {
        Real::Fixnum(_) =>
            r,
        Real::Integer(n) => {
            let max_fixnum: BigInt = FromPrimitive::from_int(isize::MAX).unwrap();
            let min_fixnum: BigInt = FromPrimitive::from_int(isize::MIN).unwrap();

            if min_fixnum <= n && n <= max_fixnum {
                Real::Fixnum(n.to_int().unwrap())
            } else {
                Real::Integer(n)
            }
        },
        Real::Rational(n) =>
            if n.is_integer() {
                reduce(Real::Integer(n.to_integer()))
            } else {
                Real::Rational(n)
            },
        Real::Flonum(_) =>
            r
    }
}

fn coerce_arith<Fix, Big, Rat, Flo>(lhs: &Real, rhs: &Real,
                                    fix_op: Fix, big_op: Big,
                                    rat_op: Rat, flo_op: Flo)
        -> Real
    where Fix: Fn(isize, isize) -> Option<isize>,
          Big: Fn(&BigInt, &BigInt) -> BigInt,
          Rat: Fn(&BigRational, &BigRational) -> BigRational,
          Flo: Fn(f64, f64) -> f64
{
    coerce(lhs, rhs,
           |x, y| match fix_op(x, y) {
               Some(n) => Real::Fixnum(n),
               None => reduce(Real::Integer(big_op(&fix2int(x), &fix2int(y))))
           },
           |x, y| reduce(Real::Integer(big_op(x, y))),
           |x, y| reduce(Real::Rational(rat_op(x, y))),
           |x, y| Real::Flonum(flo_op(x, y))
    )
}

fn coerce<Fix, Big, Rat, Flo, T>(lhs: &Real, rhs: &Real,
                                 fix_op: Fix, big_op: Big,
                                 rat_op: Rat, flo_op: Flo)
        -> T
    where Fix: Fn(isize, isize) -> T,
          Big: Fn(&BigInt, &BigInt) -> T,
          Rat: Fn(&BigRational, &BigRational) -> T,
          Flo: Fn(f64, f64) -> T
{
    match (lhs, rhs) {
        (&Real::Fixnum(l), &Real::Fixnum(r)) =>
            fix_op(l, r),
        (&Real::Fixnum(l), &Real::Integer(ref r)) =>
            big_op(&fix2int(l), r),
        (&Real::Fixnum(l), &Real::Rational(ref r)) =>
            rat_op(&fix2rat(l), r),
        (&Real::Fixnum(l), &Real::Flonum(r)) =>
            flo_op(fix2flo(l), r),
        (&Real::Integer(ref l), &Real::Fixnum(r)) =>
            big_op(l, &fix2int(r)),
        (&Real::Integer(ref l), &Real::Integer(ref r)) =>
            big_op(l, r),
        (&Real::Integer(ref l), &Real::Rational(ref r)) =>
            rat_op(&int2rat(l), r),
        (&Real::Integer(ref l), &Real::Flonum(r)) =>
            flo_op(int2flo(l), r),
        (&Real::Rational(ref l), &Real::Fixnum(r)) =>
            rat_op(l, &fix2rat(r)),
        (&Real::Rational(ref l), &Real::Integer(ref r)) =>
            rat_op(l, &int2rat(r)),
        (&Real::Rational(ref l), &Real::Rational(ref r)) =>
            rat_op(l, r),
        (&Real::Rational(ref l), &Real::Flonum(r)) =>
            flo_op(rat2flo(l), r),
        (&Real::Flonum(l), &Real::Fixnum(r)) =>
            flo_op(l, fix2flo(r)),
        (&Real::Flonum(l), &Real::Integer(ref r)) =>
            flo_op(l, int2flo(r)),
        (&Real::Flonum(l), &Real::Rational(ref r)) =>
            flo_op(l, rat2flo(r)),
        (&Real::Flonum(l), &Real::Flonum(r)) =>
            flo_op(l, r)
    }
}

macro_rules! impl_arith {
    ($tr:ident, $op:ident, $checked_op:ident) => {
        impl $tr<Real> for Real {
            type Output = Real;

            fn $op(self, other: Real) -> Real {
                coerce_arith(&self, &other,
                             |x, y| x.$checked_op(&y),
                             |x, y| x.$op(y),
                             |x, y| x.$op(y),
                             |x, y| x.$op(&y)
                )
            }
        }

        impl<'a, 'b> $tr<&'a Real> for &'b Real {
            type Output = Real;

            fn $op(self, other: &Real) -> Real {
                coerce_arith(self, other,
                             |x, y| x.$checked_op(&y),
                             |x, y| x.$op(y),
                             |x, y| x.$op(y),
                             |x, y| x.$op(&y)
                )
            }
        }
    }
}

impl_arith!(Add, add, checked_add);
impl_arith!(Sub, sub, checked_sub);
impl_arith!(Mul, mul, checked_mul);

impl Div<Real> for Real {
    type Output = Real;

    fn div(self, other: Real) -> Real {
        coerce(&self, &other,
               |x, y| Real::Rational(Ratio::new(fix2int(x), fix2int(y))),
               |x, y| Real::Rational(Ratio::new(x.clone(), y.clone())),
               |x, y| Real::Rational(x / y),
               |x, y| Real::Flonum(x / y)
        )
    }
}

impl<'a, 'b> Div<&'a Real> for &'b Real {
    type Output = Real;

    fn div(self, other: &Real) -> Real {
        coerce(self, other,
               |x, y| Real::Rational(Ratio::new(fix2int(x), fix2int(y))),
               |x, y| Real::Rational(Ratio::new(x.clone(), y.clone())),
               |x, y| Real::Rational(x / y),
               |x, y| Real::Flonum(x / y)
        )
    }
}

impl PartialEq for Real {
    fn eq(&self, other: &Real) -> bool {
        coerce(self, other,
               |x, y| x == y,
               |x, y| x == y,
               |x, y| x == y,
               |x, y| x == y
        )
    }
}

impl PartialOrd for Real {
    fn partial_cmp(&self, other: &Real) -> Option<Ordering> {
        coerce(self, other,
                    |x, y| x.partial_cmp(&y),
                    |x, y| x.partial_cmp(y),
                    |x, y| x.partial_cmp(y),
                    |x, y| x.partial_cmp(&y)
        )
    }
}

#[cfg(test)]
mod test {
    use super::{Real, int2flo, fix2int};
    use std::num::FromPrimitive;
    use num::rational::Ratio;

    #[test]
    fn test_add() {
        assert_eq!(Real::Fixnum(3), Real::Fixnum(1) + Real::Fixnum(2));
    }

    #[test]
    fn test_coerce() {
        let x = Real::Rational(Ratio::new(fix2int(3), fix2int(2)));
        let y = Real::Fixnum(1);
        let z = Real::Rational(Ratio::new(fix2int(5), fix2int(2)));
        assert_eq!(z, x + y);
    }

    #[test]
    fn test_sub() {
        assert_eq!(Real::Fixnum(1), Real::Fixnum(3) - Real::Fixnum(2));
    }

    #[test]
    fn test_mul() {
        assert_eq!(Real::Fixnum(6), Real::Fixnum(3) * Real::Fixnum(2));
    }

    #[test]
    fn test_div() {
        let x = Real::Fixnum(6);
        let y = Real::Fixnum(4);
        assert_eq!(Real::Rational(Ratio::new(fix2int(3), fix2int(2))), x/y);
    }

    #[test]
    fn test_reduce() {
        let x = Real::Integer(FromPrimitive::from_int(1).unwrap());
        let y = Real::Integer(FromPrimitive::from_int(2).unwrap());
        assert_eq!(Real::Fixnum(3), x+y);
    }

    #[test]
    fn test_reduce_rat() {
        let x = Real::Rational(Ratio::new(fix2int(3), fix2int(2)));
        let y = Real::Rational(Ratio::new(fix2int(1), fix2int(2)));
        assert_eq!(Real::Fixnum(2), x + y);
    }

    #[test]
    fn test_int2flo() {
        let n = FromPrimitive::from_int(3).unwrap();
        assert_eq!(3.0, int2flo(&n));
    }
}
<|endoftext|>