<commit_before>#[cfg(feature = "zstd")]
use zstd::block::{compress, decompress};

use crate::*;

use super::{
    arr_to_u32, pwrite_all, raw_segment_iter_from, u32_to_arr, u64_to_arr,
    DiskPtr, LogIter, LogKind, LogOffset, Lsn, MessageKind, MAX_MSG_HEADER_LEN,
};

/// A snapshot of the state required to quickly restart
/// the `PageCache` and `SegmentAccountant`.
#[derive(Debug, Default)]
#[cfg_attr(test, derive(Clone, PartialEq))]
pub struct Snapshot {
    /// The last read message lsn
    pub last_lsn: Lsn,
    /// The last read message lid
    pub last_lid: LogOffset,
    /// The highest stable offset persisted
    /// into a segment header
    pub max_header_stable_lsn: Lsn,
    /// the mapping from pages to (lsn, lid)
    pub pt: Vec<PageState>,
}

#[derive(Clone, Debug, PartialEq)]
pub enum PageState {
    Present(Vec<(Lsn, DiskPtr, u64)>),
    Free(Lsn, DiskPtr),
    Uninitialized,
}

impl PageState {
    fn push(&mut self, item: (Lsn, DiskPtr, u64)) {
        match *self {
            PageState::Present(ref mut items) => items.push(item),
            _ => panic!("pushed items to {:?}", self),
        }
    }

    /// Iterate over the (lsn, lid) pairs that hold this page's state.
    pub fn iter(&self) -> impl Iterator<Item = (Lsn, DiskPtr, u64)> {
        match *self {
            PageState::Present(ref items) => items.clone().into_iter(),
            PageState::Free(lsn, ptr) => {
                vec![(lsn, ptr, u64::try_from(MAX_MSG_HEADER_LEN).unwrap())]
                    .into_iter()
            }
            PageState::Uninitialized => panic!("canned iter on a {:?}", self),
        }
    }

    pub(crate) fn is_free(&self) -> bool {
        match *self {
            PageState::Free(_, _) => true,
            _ => false,
        }
    }
}

impl Snapshot {
    fn apply(
        &mut self,
        log_kind: LogKind,
        pid: PageId,
        lsn: Lsn,
        disk_ptr: DiskPtr,
        sz: u64,
    ) {
        // unwrapping this because it's already passed the crc check
        // in the log iterator
        trace!("trying to deserialize buf for ptr {} lsn {}", disk_ptr, lsn);
        let _measure = Measure::new(&M.snapshot_apply);

        if self.pt.len() <= usize::try_from(pid).unwrap() {
            self.pt.resize(
                usize::try_from(pid + 1).unwrap(),
                PageState::Uninitialized,
            );
        }

        match log_kind {
            LogKind::Replace => {
                trace!(
                    "compact of pid {} at ptr {} lsn {}",
                    pid,
                    disk_ptr,
                    lsn,
                );

                self.pt[usize::try_from(pid).unwrap()] =
                    PageState::Present(vec![(lsn, disk_ptr, sz)]);
            }
            LogKind::Link => {
                // Because we rewrite pages over time, we may have relocated
                // a page's initial Compact to a later segment. We should skip
                // over pages here unless we've encountered a Compact for them.
                if let Some(lids) =
                    self.pt.get_mut(usize::try_from(pid).unwrap())
                {
                    trace!(
                        "append of pid {} at lid {} lsn {}",
                        pid,
                        disk_ptr,
                        lsn,
                    );

                    if lids.is_free() {
                        // this can happen if the allocate or replace
                        // has been moved to a later segment.

                        trace!(
                            "we have not yet encountered an \
                             allocation of this page, skipping push"
                        );

                        return;
                    }

                    lids.push((lsn, disk_ptr, sz));
                } else {
                    trace!(
                        "skipping dangling append of pid {} at lid {} lsn {}",
                        pid,
                        disk_ptr,
                        lsn,
                    );
                }
            }
            LogKind::Free => {
                trace!("free of pid {} at ptr {} lsn {}", pid, disk_ptr, lsn);
                self.pt[usize::try_from(pid).unwrap()] =
                    PageState::Free(lsn, disk_ptr);
            }
            LogKind::Corrupted | LogKind::Skip => panic!(
                "unexppected messagekind in snapshot application: {:?}",
                log_kind
            ),
        }
    }
}

pub(crate) fn advance_snapshot(
    iter: LogIter,
    mut snapshot: Snapshot,
    config: &RunningConfig,
) -> Result<Snapshot> {
    let _measure = Measure::new(&M.advance_snapshot);

    trace!("building on top of old snapshot: {:?}", snapshot);

    let old_lsn = snapshot.last_lsn;

    for (log_kind, pid, lsn, ptr, sz) in iter {
        trace!(
            "in advance_snapshot looking at item with lsn {} ptr {}",
            lsn,
            ptr
        );

        if lsn <= snapshot.last_lsn {
            // don't process already-processed Lsn's. last_lsn is for the last
            // item ALREADY INCLUDED lsn in the snapshot.
            trace!(
                "continuing in advance_snapshot, lsn {} ptr {} last_lsn {}",
                lsn,
                ptr,
                snapshot.last_lsn
            );
            continue;
        }

        assert!(lsn > snapshot.last_lsn);
        snapshot.last_lsn = lsn;
        snapshot.last_lid = ptr.lid();

        snapshot.apply(log_kind, pid, lsn, ptr, sz);
    }

    if snapshot.last_lsn != old_lsn {
        write_snapshot(config, &snapshot)?;
    }

    trace!("generated new snapshot: {:?}", snapshot);

    #[cfg(feature = "event_log")]
    config.event_log.recovered_lsn(snapshot.last_lsn);

    Ok(snapshot)
}

/// Read a `Snapshot` or generate a default, then advance it to
/// the tip of the data file, if present.
pub fn read_snapshot_or_default(config: &RunningConfig) -> Result<Snapshot> {
    let mut last_snap =
        read_snapshot(config)?.unwrap_or_else(Snapshot::default);

    let (log_iter, max_header_stable_lsn, to_zero) =
        raw_segment_iter_from(last_snap.last_lsn, config)?;

    last_snap.max_header_stable_lsn = max_header_stable_lsn;

    let res = advance_snapshot(log_iter, last_snap, config)?;

    for lid in to_zero {
        debug!("zeroing torn segment at lid {}", lid);

        // NB we intentionally corrupt this header to prevent any segment
        // from being allocated which would duplicate its LSN, messing
        // up recovery in the future.
        maybe_fail!("segment initial free zero");
        pwrite_all(
            &config.file,
            &*vec![MessageKind::Corrupted.into(); SEG_HEADER_LEN],
            lid,
        )?;
        if !config.temporary {
            config.file.sync_all()?;
        }
    }

    Ok(res)
}

/// Read a `Snapshot` from disk.
fn read_snapshot(config: &RunningConfig) -> std::io::Result<Option<Snapshot>> {
    let mut f = loop {
        let mut candidates = config.get_snapshot_files()?;
        if candidates.is_empty() {
            debug!("no previous snapshot found");
            return Ok(None);
        }

        candidates.sort();

        let path = candidates.pop().unwrap();

        match std::fs::OpenOptions::new().read(true).open(&path) {
            Ok(f) => break f,
            Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => {
                // this can happen if there's a race
                continue;
            }
            Err(other) => return Err(other),
        }
    };

    if f.metadata()?.len() <= 12 {
        warn!("empty/corrupt snapshot file found");
        return Ok(None);
    }

    let mut buf = vec![];
    let _read = f.read_to_end(&mut buf)?;
    let len = buf.len();
    let mut len_expected_bytes = [0; 8];
    len_expected_bytes.copy_from_slice(&buf[len - 12..len - 4]);

    let mut crc_expected_bytes = [0; 4];
    crc_expected_bytes.copy_from_slice(&buf[len - 4..]);

    let _ = buf.split_off(len - 12);
    let crc_expected: u32 = arr_to_u32(&crc_expected_bytes);

    let crc_actual = crc32(&buf);

    if crc_expected != crc_actual {
        return Ok(None);
    }

    #[cfg(feature = "zstd")]
    let bytes = if config.use_compression {
        use std::convert::TryInto;

        let len_expected: u64 =
            u64::from_le_bytes(len_expected_bytes.as_ref().try_into().unwrap());

        decompress(&*buf, usize::try_from(len_expected).unwrap()).unwrap()
    } else {
        buf
    };

    #[cfg(not(feature = "zstd"))]
    let bytes = buf;

    Ok(Snapshot::deserialize(&mut bytes.as_slice()).ok())
}

fn write_snapshot(config: &RunningConfig, snapshot: &Snapshot) -> Result<()> {
    let raw_bytes = snapshot.serialize();
    let decompressed_len = raw_bytes.len();

    #[cfg(feature = "zstd")]
    let bytes = if config.use_compression {
        compress(&*raw_bytes, config.compression_factor).unwrap()
    } else {
        raw_bytes
    };

    #[cfg(not(feature = "zstd"))]
    let bytes = raw_bytes;

    let crc32: [u8; 4] = u32_to_arr(crc32(&bytes));
    let len_bytes: [u8; 8] = u64_to_arr(decompressed_len as u64);

    let path_1_suffix = format!("snap.{:016X}.generating", snapshot.last_lsn);

    let mut path_1 = config.get_path();
    path_1.push(path_1_suffix);

    let path_2_suffix = format!("snap.{:016X}", snapshot.last_lsn);

    let mut path_2 = config.get_path();
    path_2.push(path_2_suffix);

    let parent = path_1.parent().unwrap();
    std::fs::create_dir_all(parent)?;
    let mut f =
        std::fs::OpenOptions::new().write(true).create(true).open(&path_1)?;

    // write the snapshot bytes, followed by a crc64 checksum at the end
    maybe_fail!("snap write");
    f.write_all(&*bytes)?;
    maybe_fail!("snap write len");
    f.write_all(&len_bytes)?;
    maybe_fail!("snap write crc");
    f.write_all(&crc32)?;
    maybe_fail!("snap write post");

    trace!("wrote snapshot to {}", path_1.to_string_lossy());

    maybe_fail!("snap write mv");
    std::fs::rename(&path_1, &path_2)?;
    maybe_fail!("snap write mv post");

    trace!("renamed snapshot to {}", path_2.to_string_lossy());

    // clean up any old snapshots
    let candidates = config.get_snapshot_files()?;
    for path in candidates {
        let path_str = path.file_name().unwrap().to_str().unwrap();
        if !path_2.to_string_lossy().ends_with(&*path_str) {
            debug!("removing old snapshot file {:?}", path);

            maybe_fail!("snap write rm old");

            if let Err(e) = std::fs::remove_file(&path) {
                // TODO should this just be a try return?
                warn!(
                    "failed to remove old snapshot file, maybe snapshot race? {}",
                    e
                );
            }
        }
    }
    Ok(())
}
<commit_msg>Only try to add items to a present page state<commit_after>#[cfg(feature = "zstd")]
use zstd::block::{compress, decompress};

use crate::*;

use super::{
    arr_to_u32, pwrite_all, raw_segment_iter_from, u32_to_arr, u64_to_arr,
    DiskPtr, LogIter, LogKind, LogOffset, Lsn, MessageKind, MAX_MSG_HEADER_LEN,
};

/// A snapshot of the state required to quickly restart
/// the `PageCache` and `SegmentAccountant`.
#[derive(Debug, Default)]
#[cfg_attr(test, derive(Clone, PartialEq))]
pub struct Snapshot {
    /// The last read message lsn
    pub last_lsn: Lsn,
    /// The last read message lid
    pub last_lid: LogOffset,
    /// The highest stable offset persisted
    /// into a segment header
    pub max_header_stable_lsn: Lsn,
    /// the mapping from pages to (lsn, lid)
    pub pt: Vec<PageState>,
}

#[derive(Clone, Debug, PartialEq)]
pub enum PageState {
    Present(Vec<(Lsn, DiskPtr, u64)>),
    Free(Lsn, DiskPtr),
    Uninitialized,
}

impl PageState {
    fn push(&mut self, item: (Lsn, DiskPtr, u64)) {
        match *self {
            PageState::Present(ref mut items) => items.push(item),
            _ => panic!("pushed items to {:?}", self),
        }
    }

    /// Iterate over the (lsn, lid) pairs that hold this page's state.
    pub fn iter(&self) -> impl Iterator<Item = (Lsn, DiskPtr, u64)> {
        match *self {
            PageState::Present(ref items) => items.clone().into_iter(),
            PageState::Free(lsn, ptr) => {
                vec![(lsn, ptr, u64::try_from(MAX_MSG_HEADER_LEN).unwrap())]
                    .into_iter()
            }
            PageState::Uninitialized => panic!("canned iter on a {:?}", self),
        }
    }

    pub(crate) fn is_free(&self) -> bool {
        match *self {
            PageState::Free(_, _) => true,
            _ => false,
        }
    }
}

impl Snapshot {
    fn apply(
        &mut self,
        log_kind: LogKind,
        pid: PageId,
        lsn: Lsn,
        disk_ptr: DiskPtr,
        sz: u64,
    ) {
        // unwrapping this because it's already passed the crc check
        // in the log iterator
        trace!("trying to deserialize buf for ptr {} lsn {}", disk_ptr, lsn);
        let _measure = Measure::new(&M.snapshot_apply);

        if self.pt.len() <= usize::try_from(pid).unwrap() {
            self.pt.resize(
                usize::try_from(pid + 1).unwrap(),
                PageState::Uninitialized,
            );
        }

        match log_kind {
            LogKind::Replace => {
                trace!(
                    "compact of pid {} at ptr {} lsn {}",
                    pid,
                    disk_ptr,
                    lsn,
                );

                self.pt[usize::try_from(pid).unwrap()] =
                    PageState::Present(vec![(lsn, disk_ptr, sz)]);
            }
            LogKind::Link => {
                // Because we rewrite pages over time, we may have relocated
                // a page's initial Compact to a later segment. We should skip
                // over pages here unless we've encountered a Compact for them.
                if let Some(lids @ PageState::Present(_)) =
                    self.pt.get_mut(usize::try_from(pid).unwrap())
                {
                    trace!(
                        "append of pid {} at lid {} lsn {}",
                        pid,
                        disk_ptr,
                        lsn,
                    );

                    if lids.is_free() {
                        // this can happen if the allocate or replace
                        // has been moved to a later segment.

                        trace!(
                            "we have not yet encountered an \
                             allocation of this page, skipping push"
                        );

                        return;
                    }

                    lids.push((lsn, disk_ptr, sz));
                } else {
                    trace!(
                        "skipping dangling append of pid {} at lid {} lsn {}",
                        pid,
                        disk_ptr,
                        lsn,
                    );
                }
            }
            LogKind::Free => {
                trace!("free of pid {} at ptr {} lsn {}", pid, disk_ptr, lsn);
                self.pt[usize::try_from(pid).unwrap()] =
                    PageState::Free(lsn, disk_ptr);
            }
            LogKind::Corrupted | LogKind::Skip => panic!(
                "unexppected messagekind in snapshot application: {:?}",
                log_kind
            ),
        }
    }
}

pub(crate) fn advance_snapshot(
    iter: LogIter,
    mut snapshot: Snapshot,
    config: &RunningConfig,
) -> Result<Snapshot> {
    let _measure = Measure::new(&M.advance_snapshot);

    trace!("building on top of old snapshot: {:?}", snapshot);

    let old_lsn = snapshot.last_lsn;

    for (log_kind, pid, lsn, ptr, sz) in iter {
        trace!(
            "in advance_snapshot looking at item with lsn {} ptr {}",
            lsn,
            ptr
        );

        if lsn <= snapshot.last_lsn {
            // don't process already-processed Lsn's. last_lsn is for the last
            // item ALREADY INCLUDED lsn in the snapshot.
            trace!(
                "continuing in advance_snapshot, lsn {} ptr {} last_lsn {}",
                lsn,
                ptr,
                snapshot.last_lsn
            );
            continue;
        }

        assert!(lsn > snapshot.last_lsn);
        snapshot.last_lsn = lsn;
        snapshot.last_lid = ptr.lid();

        snapshot.apply(log_kind, pid, lsn, ptr, sz);
    }

    if snapshot.last_lsn != old_lsn {
        write_snapshot(config, &snapshot)?;
    }

    trace!("generated new snapshot: {:?}", snapshot);

    #[cfg(feature = "event_log")]
    config.event_log.recovered_lsn(snapshot.last_lsn);

    Ok(snapshot)
}

/// Read a `Snapshot` or generate a default, then advance it to
/// the tip of the data file, if present.
pub fn read_snapshot_or_default(config: &RunningConfig) -> Result<Snapshot> {
    let mut last_snap =
        read_snapshot(config)?.unwrap_or_else(Snapshot::default);

    let (log_iter, max_header_stable_lsn, to_zero) =
        raw_segment_iter_from(last_snap.last_lsn, config)?;

    last_snap.max_header_stable_lsn = max_header_stable_lsn;

    let res = advance_snapshot(log_iter, last_snap, config)?;

    for lid in to_zero {
        debug!("zeroing torn segment at lid {}", lid);

        // NB we intentionally corrupt this header to prevent any segment
        // from being allocated which would duplicate its LSN, messing
        // up recovery in the future.
        maybe_fail!("segment initial free zero");
        pwrite_all(
            &config.file,
            &*vec![MessageKind::Corrupted.into(); SEG_HEADER_LEN],
            lid,
        )?;
        if !config.temporary {
            config.file.sync_all()?;
        }
    }

    Ok(res)
}

/// Read a `Snapshot` from disk.
fn read_snapshot(config: &RunningConfig) -> std::io::Result<Option<Snapshot>> {
    let mut f = loop {
        let mut candidates = config.get_snapshot_files()?;
        if candidates.is_empty() {
            debug!("no previous snapshot found");
            return Ok(None);
        }

        candidates.sort();

        let path = candidates.pop().unwrap();

        match std::fs::OpenOptions::new().read(true).open(&path) {
            Ok(f) => break f,
            Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => {
                // this can happen if there's a race
                continue;
            }
            Err(other) => return Err(other),
        }
    };

    if f.metadata()?.len() <= 12 {
        warn!("empty/corrupt snapshot file found");
        return Ok(None);
    }

    let mut buf = vec![];
    let _read = f.read_to_end(&mut buf)?;
    let len = buf.len();
    let mut len_expected_bytes = [0; 8];
    len_expected_bytes.copy_from_slice(&buf[len - 12..len - 4]);

    let mut crc_expected_bytes = [0; 4];
    crc_expected_bytes.copy_from_slice(&buf[len - 4..]);

    let _ = buf.split_off(len - 12);
    let crc_expected: u32 = arr_to_u32(&crc_expected_bytes);

    let crc_actual = crc32(&buf);

    if crc_expected != crc_actual {
        return Ok(None);
    }

    #[cfg(feature = "zstd")]
    let bytes = if config.use_compression {
        use std::convert::TryInto;

        let len_expected: u64 =
            u64::from_le_bytes(len_expected_bytes.as_ref().try_into().unwrap());

        decompress(&*buf, usize::try_from(len_expected).unwrap()).unwrap()
    } else {
        buf
    };

    #[cfg(not(feature = "zstd"))]
    let bytes = buf;

    Ok(Snapshot::deserialize(&mut bytes.as_slice()).ok())
}

fn write_snapshot(config: &RunningConfig, snapshot: &Snapshot) -> Result<()> {
    let raw_bytes = snapshot.serialize();
    let decompressed_len = raw_bytes.len();

    #[cfg(feature = "zstd")]
    let bytes = if config.use_compression {
        compress(&*raw_bytes, config.compression_factor).unwrap()
    } else {
        raw_bytes
    };

    #[cfg(not(feature = "zstd"))]
    let bytes = raw_bytes;

    let crc32: [u8; 4] = u32_to_arr(crc32(&bytes));
    let len_bytes: [u8; 8] = u64_to_arr(decompressed_len as u64);

    let path_1_suffix = format!("snap.{:016X}.generating", snapshot.last_lsn);

    let mut path_1 = config.get_path();
    path_1.push(path_1_suffix);

    let path_2_suffix = format!("snap.{:016X}", snapshot.last_lsn);

    let mut path_2 = config.get_path();
    path_2.push(path_2_suffix);

    let parent = path_1.parent().unwrap();
    std::fs::create_dir_all(parent)?;
    let mut f =
        std::fs::OpenOptions::new().write(true).create(true).open(&path_1)?;

    // write the snapshot bytes, followed by a crc64 checksum at the end
    maybe_fail!("snap write");
    f.write_all(&*bytes)?;
    maybe_fail!("snap write len");
    f.write_all(&len_bytes)?;
    maybe_fail!("snap write crc");
    f.write_all(&crc32)?;
    maybe_fail!("snap write post");

    trace!("wrote snapshot to {}", path_1.to_string_lossy());

    maybe_fail!("snap write mv");
    std::fs::rename(&path_1, &path_2)?;
    maybe_fail!("snap write mv post");

    trace!("renamed snapshot to {}", path_2.to_string_lossy());

    // clean up any old snapshots
    let candidates = config.get_snapshot_files()?;
    for path in candidates {
        let path_str = path.file_name().unwrap().to_str().unwrap();
        if !path_2.to_string_lossy().ends_with(&*path_str) {
            debug!("removing old snapshot file {:?}", path);

            maybe_fail!("snap write rm old");

            if let Err(e) = std::fs::remove_file(&path) {
                // TODO should this just be a try return?
                warn!(
                    "failed to remove old snapshot file, maybe snapshot race? {}",
                    e
                );
            }
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>
use std::iter::Peekable;
use PutBack;
use PutBackN;

/// An iterator that allows peeking at an element before deciding to accept it.
///
/// See [`.peeking_take_while()`](trait.Itertools.html#method.peeking_take_while)
/// for more information.
///
/// This is implemented by peeking adaptors like peekable and put back,
/// but also by a few iterators that can be peeked natively, like the slice’s
/// by reference iterator (`std::slice::Iter`).
pub trait PeekingNext : Iterator {
    /// Pass a reference to the next iterator element to the closure `accept`;
    /// if `accept` returns true, return it as the next element,
    /// else None.
    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
        where F: FnOnce(&Self::Item) -> bool;
}

impl<I> PeekingNext for Peekable<I>
    where I: Iterator,
{
    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
        where F: FnOnce(&Self::Item) -> bool
    {
        if let Some(r) = self.peek() {
            if !accept(r) {
                return None;
            }
        }
        self.next()
    }
}

impl<I> PeekingNext for PutBack<I>
    where I: Iterator,
{
    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
        where F: FnOnce(&Self::Item) -> bool
    {
        if let Some(r) = self.next() {
            if !accept(&r) {
                self.put_back(r);
                return None;
            }
            Some(r)
        } else {
            None
        }
    }
}

impl<I> PeekingNext for PutBackN<I>
    where I: Iterator,
{
    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
        where F: FnOnce(&Self::Item) -> bool
    {
        if let Some(r) = self.next() {
            if !accept(&r) {
                self.put_back(r);
                return None;
            }
            Some(r)
        } else {
            None
        }
    }
}

/// An iterator adaptor that takes items while a closure returns `true`.
///
/// See [`.peeking_take_while()`](../trait.Itertools.html#method.peeking_take_while)
/// for more information.
pub struct PeekingTakeWhile<'a, I: 'a, F>
    where I: Iterator,
{
    iter: &'a mut I,
    f: F,
}

/// Create a PeekingTakeWhile
pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F>
    where I: Iterator,
{
    PeekingTakeWhile {
        iter: iter,
        f: f,
    }
}

impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F>
    where I: PeekingNext,
          F: FnMut(&I::Item) -> bool,

{
    type Item = I::Item;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.peeking_next(&mut self.f)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let (_, hi) = self.iter.size_hint();
        (0, hi)
    }
}

// Some iterators are so lightweight we can simply clone them to save their
// state and use that for peeking.
macro_rules! peeking_next_by_clone {
    (@as_item $x:item) => ($x);
    ([$($typarm:tt)*] $type_:ty) => {
        impl<$($typarm)*> PeekingNext for $type_ {
            fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
                where F: FnOnce(&Self::Item) -> bool
            {
                let saved_state = self.clone();
                if let Some(r) = self.next() {
                    if !accept(&r) {
                        *self = saved_state;
                    } else {
                        return Some(r)
                    }
                }
                None
            }
        }
    }
}

peeking_next_by_clone! { ['a, T] ::std::slice::Iter<'a, T> }
peeking_next_by_clone! { ['a] ::std::str::Chars<'a> }
peeking_next_by_clone! { ['a] ::std::str::CharIndices<'a> }
peeking_next_by_clone! { ['a] ::std::str::Bytes<'a> }
peeking_next_by_clone! { ['a, T] ::std::option::Iter<'a, T> }
peeking_next_by_clone! { ['a, T] ::std::result::Iter<'a, T> }
peeking_next_by_clone! { [T] ::std::iter::Empty<T> }
peeking_next_by_clone! { ['a, T] ::std::collections::linked_list::Iter<'a, T> }
peeking_next_by_clone! { ['a, T] ::std::collections::vec_deque::Iter<'a, T> }

// cloning a Rev has no extra overhead; peekable and put backs are never DEI.
peeking_next_by_clone! { [I: Clone + PeekingNext + DoubleEndedIterator]
                         ::std::iter::Rev<I> }
<commit_msg>MAINT: Remove unused macro arm<commit_after>
use std::iter::Peekable;
use PutBack;
use PutBackN;

/// An iterator that allows peeking at an element before deciding to accept it.
///
/// See [`.peeking_take_while()`](trait.Itertools.html#method.peeking_take_while)
/// for more information.
///
/// This is implemented by peeking adaptors like peekable and put back,
/// but also by a few iterators that can be peeked natively, like the slice’s
/// by reference iterator (`std::slice::Iter`).
pub trait PeekingNext : Iterator {
    /// Pass a reference to the next iterator element to the closure `accept`;
    /// if `accept` returns true, return it as the next element,
    /// else None.
    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
        where F: FnOnce(&Self::Item) -> bool;
}

impl<I> PeekingNext for Peekable<I>
    where I: Iterator,
{
    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
        where F: FnOnce(&Self::Item) -> bool
    {
        if let Some(r) = self.peek() {
            if !accept(r) {
                return None;
            }
        }
        self.next()
    }
}

impl<I> PeekingNext for PutBack<I>
    where I: Iterator,
{
    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
        where F: FnOnce(&Self::Item) -> bool
    {
        if let Some(r) = self.next() {
            if !accept(&r) {
                self.put_back(r);
                return None;
            }
            Some(r)
        } else {
            None
        }
    }
}

impl<I> PeekingNext for PutBackN<I>
    where I: Iterator,
{
    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
        where F: FnOnce(&Self::Item) -> bool
    {
        if let Some(r) = self.next() {
            if !accept(&r) {
                self.put_back(r);
                return None;
            }
            Some(r)
        } else {
            None
        }
    }
}

/// An iterator adaptor that takes items while a closure returns `true`.
///
/// See [`.peeking_take_while()`](../trait.Itertools.html#method.peeking_take_while)
/// for more information.
pub struct PeekingTakeWhile<'a, I: 'a, F>
    where I: Iterator,
{
    iter: &'a mut I,
    f: F,
}

/// Create a PeekingTakeWhile
pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F>
    where I: Iterator,
{
    PeekingTakeWhile {
        iter: iter,
        f: f,
    }
}

impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F>
    where I: PeekingNext,
          F: FnMut(&I::Item) -> bool,

{
    type Item = I::Item;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.peeking_next(&mut self.f)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let (_, hi) = self.iter.size_hint();
        (0, hi)
    }
}

// Some iterators are so lightweight we can simply clone them to save their
// state and use that for peeking.
macro_rules! peeking_next_by_clone {
    ([$($typarm:tt)*] $type_:ty) => {
        impl<$($typarm)*> PeekingNext for $type_ {
            fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
                where F: FnOnce(&Self::Item) -> bool
            {
                let saved_state = self.clone();
                if let Some(r) = self.next() {
                    if !accept(&r) {
                        *self = saved_state;
                    } else {
                        return Some(r)
                    }
                }
                None
            }
        }
    }
}

peeking_next_by_clone! { ['a, T] ::std::slice::Iter<'a, T> }
peeking_next_by_clone! { ['a] ::std::str::Chars<'a> }
peeking_next_by_clone! { ['a] ::std::str::CharIndices<'a> }
peeking_next_by_clone! { ['a] ::std::str::Bytes<'a> }
peeking_next_by_clone! { ['a, T] ::std::option::Iter<'a, T> }
peeking_next_by_clone! { ['a, T] ::std::result::Iter<'a, T> }
peeking_next_by_clone! { [T] ::std::iter::Empty<T> }
peeking_next_by_clone! { ['a, T] ::std::collections::linked_list::Iter<'a, T> }
peeking_next_by_clone! { ['a, T] ::std::collections::vec_deque::Iter<'a, T> }

// cloning a Rev has no extra overhead; peekable and put backs are never DEI.
peeking_next_by_clone! { [I: Clone + PeekingNext + DoubleEndedIterator]
                         ::std::iter::Rev<I> }
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern: is not UTF-8

fn foo() {
    include!("not-utf8.bin")
}
<commit_msg>test/compile-fail: fix expected error message for non-UTF8 source.<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern: did not contain valid UTF-8

fn foo() {
    include!("not-utf8.bin")
}
<|endoftext|>


<commit_before>// -*- rust -*-

// Tests of ports and channels on various types

impure fn test_rec() {
  type r = rec(int val0, u8 val1, char val2);

  let port[r] po = port();
  let chan[r] ch = chan(po);
  let r r0 = rec(val0 = 0, val1 = 1u8, val2 = '2');

  ch <| r0;

  let r r1;
  r1 <- po;

  check (r1.val0 == 0);
  check (r1.val1 == 1u8);
  check (r1.val2 == '2');
}

impure fn test_vec() {
  let port[vec[int]] po = port();
  let chan[vec[int]] ch = chan(po);
  let vec[int] v0 = vec(0, 1, 2);

  ch <| v0;

  let vec[int] v1;
  v1 <- po;

  check (v1.(0) == 0);
  check (v1.(1) == 1);
  check (v1.(2) == 2);
}

impure fn test_tup() {
  type t = tup(int, u8, char);

  let port[t] po = port();
  let chan[t] ch = chan(po);
  let t t0 = tup(0, 1u8, '2');

  ch <| t0;

  let t t1;
  t1 <- po;

  check (t0._0 == 0);
  check (t0._1 == 1u8);
  check (t0._2 == '2');
}

impure fn test_tag() {
  tag t {
    tag1;
    tag2(int);
    tag3(int, u8, char);
  }

  let port[t] po = port();
  let chan[t] ch = chan(po);

  ch <| tag1;
  ch <| tag2(10);
  ch <| tag3(10, 11u8, 'A');

  let t t1;

  t1 <- po;
  check (t1 == tag1);
  t1 <- po;
  check (t1 == tag2(10));
  t1 <- po;
  check (t1 == tag3(10, 11u8, 'A'));
}

impure fn test_chan() {
  let port[chan[int]] po = port();
  let chan[chan[int]] ch = chan(po);

  let port[int] po0 = port();
  let chan[int] ch0 = chan(po0);

  ch <| ch0;

  let chan[int] ch1;
  ch1 <- po;

  // Does the transmitted channel still work?
  ch1 <| 10;

  let int i;
  i <- po0;

  check (i == 10);
}

impure fn main() {
  test_rec();
  test_vec();
  test_tup();
  test_tag();
  test_chan();
}
<commit_msg>Add test for sending strings over channels<commit_after>// -*- rust -*-

// Tests of ports and channels on various types

impure fn test_rec() {
  type r = rec(int val0, u8 val1, char val2);

  let port[r] po = port();
  let chan[r] ch = chan(po);
  let r r0 = rec(val0 = 0, val1 = 1u8, val2 = '2');

  ch <| r0;

  let r r1;
  r1 <- po;

  check (r1.val0 == 0);
  check (r1.val1 == 1u8);
  check (r1.val2 == '2');
}

impure fn test_vec() {
  let port[vec[int]] po = port();
  let chan[vec[int]] ch = chan(po);
  let vec[int] v0 = vec(0, 1, 2);

  ch <| v0;

  let vec[int] v1;
  v1 <- po;

  check (v1.(0) == 0);
  check (v1.(1) == 1);
  check (v1.(2) == 2);
}

impure fn test_str() {
  let port[str] po = port();
  let chan[str] ch = chan(po);
  let str s0 = "test";

  ch <| s0;

  let str s1;
  s1 <- po;

  check (s1.(0) as u8 == 't' as u8);
  check (s1.(1) as u8 == 'e' as u8);
  check (s1.(2) as u8 == 's' as u8);
  check (s1.(3) as u8 == 't' as u8);
}

impure fn test_tup() {
  type t = tup(int, u8, char);

  let port[t] po = port();
  let chan[t] ch = chan(po);
  let t t0 = tup(0, 1u8, '2');

  ch <| t0;

  let t t1;
  t1 <- po;

  check (t0._0 == 0);
  check (t0._1 == 1u8);
  check (t0._2 == '2');
}

impure fn test_tag() {
  tag t {
    tag1;
    tag2(int);
    tag3(int, u8, char);
  }

  let port[t] po = port();
  let chan[t] ch = chan(po);

  ch <| tag1;
  ch <| tag2(10);
  ch <| tag3(10, 11u8, 'A');

  let t t1;

  t1 <- po;
  check (t1 == tag1);
  t1 <- po;
  check (t1 == tag2(10));
  t1 <- po;
  check (t1 == tag3(10, 11u8, 'A'));
}

impure fn test_chan() {
  let port[chan[int]] po = port();
  let chan[chan[int]] ch = chan(po);

  let port[int] po0 = port();
  let chan[int] ch0 = chan(po0);

  ch <| ch0;

  let chan[int] ch1;
  ch1 <- po;

  // Does the transmitted channel still work?
  ch1 <| 10;

  let int i;
  i <- po0;

  check (i == 10);
}

impure fn main() {
  test_rec();
  test_vec();
  test_str();
  test_tup();
  test_tag();
  test_chan();
}
<|endoftext|>


<commit_before>pub fn normalize(num: &str) -> String {
    let num = num.replace("IIIII", "V");
    let num = num.replace("IIII", "IV");
    let num = num.replace("VIV", "IX");
    let num = num.replace("VV", "X");
    let num = num.replace("XXXXX", "L");
    let num = num.replace("XXXX", "XL");
    let num = num.replace("LXL", "XC");
    let num = num.replace("LL", "C");
    let num = num.replace("CCCCC", "D");
    let num = num.replace("CCCC", "CD");
    num
}

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

    #[test]
    fn iiiii_becomes_v() {
        assert_eq!("V", normalize("IIIII"));
    }

    #[test]
    fn iiii_becomes_iv() {
        assert_eq!("IV", normalize("IIII"));
    }

    #[test]
    fn vv_becomes_x() {
        assert_eq!("X", normalize("VV"));
    }

    #[test]
    fn viiii_becomes_ix() {
        assert_eq!("IX", normalize("VIIII"));
    }

    #[test]
    fn viiiii_becomes_x() {
        assert_eq!("X", normalize("VIIIII"));
    }

    #[test]
    fn vviv_becomes_xiv() {
        assert_eq!("XIV", normalize("VVIV"));
    }

    #[test]
    fn xxxxx_becomes_l() {
        assert_eq!("L", normalize("XXXXX"));
    }

    #[test]
    fn xxxx_becomes_xl() {
        assert_eq!("XL", normalize("XXXX"));
    }

    #[test]
    fn lxxxx_becomes_xc() {
        assert_eq!("XC", normalize("LXXXX"));
    }

    #[test]
    fn lxxxxx_becomes_c() {
        assert_eq!("C", normalize("LXXXXX"));
    }

    #[test]
    fn cccc_becomes_cd() {
        assert_eq!("CD", normalize("CCCC"));
    }

    #[test]
    fn ccccc_becomes_d() {
        assert_eq!("D", normalize("CCCCC"));
    }
}
<commit_msg>Fix VVIV bug in Rust also<commit_after>pub fn normalize(num: &str) -> String {
    let num = num.replace("IIIII", "V");
    let num = num.replace("IIII", "IV");
    let num = num.replace("VV", "X");
    let num = num.replace("VIV", "IX");
    let num = num.replace("XXXXX", "L");
    let num = num.replace("XXXX", "XL");
    let num = num.replace("LL", "C");
    let num = num.replace("LXL", "XC");
    let num = num.replace("CCCCC", "D");
    let num = num.replace("CCCC", "CD");
    num
}

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

    #[test]
    fn iiiii_becomes_v() {
        assert_eq!("V", normalize("IIIII"));
    }

    #[test]
    fn iiii_becomes_iv() {
        assert_eq!("IV", normalize("IIII"));
    }

    #[test]
    fn vv_becomes_x() {
        assert_eq!("X", normalize("VV"));
    }

    #[test]
    fn viiii_becomes_ix() {
        assert_eq!("IX", normalize("VIIII"));
    }

    #[test]
    fn viiiii_becomes_x() {
        assert_eq!("X", normalize("VIIIII"));
    }

    #[test]
    fn vviv_becomes_xiv() {
        assert_eq!("XIV", normalize("VVIV"));
    }

    #[test]
    fn xxxxx_becomes_l() {
        assert_eq!("L", normalize("XXXXX"));
    }

    #[test]
    fn xxxx_becomes_xl() {
        assert_eq!("XL", normalize("XXXX"));
    }

    #[test]
    fn lxxxx_becomes_xc() {
        assert_eq!("XC", normalize("LXXXX"));
    }

    #[test]
    fn lxxxxx_becomes_c() {
        assert_eq!("C", normalize("LXXXXX"));
    }

    #[test]
    fn llxl_becomes_cxl() {
        assert_eq!("CXL", normalize("LLXL"));
    }

    #[test]
    fn cccc_becomes_cd() {
        assert_eq!("CD", normalize("CCCC"));
    }

    #[test]
    fn ccccc_becomes_d() {
        assert_eq!("D", normalize("CCCCC"));
    }
}
<|endoftext|>


<commit_before>#[doc =
  "Pulls type information out of the AST and attaches it to the document"];

import rustc::syntax::ast;
import rustc::syntax::print::pprust;
import rustc::middle::ast_map;

export mk_pass;

fn mk_pass() -> pass {
    run
}

fn run(
    srv: astsrv::srv,
    doc: doc::cratedoc
) -> doc::cratedoc {
    let fold = fold::fold({
        fold_fn: fold_fn,
        fold_const: fold_const,
        fold_enum: fold_enum,
        fold_res: fold_res
        with *fold::default_seq_fold(srv)
    });
    fold.fold_crate(fold, doc)
}

fn fold_fn(
    fold: fold::fold<astsrv::srv>,
    doc: doc::fndoc
) -> doc::fndoc {

    let srv = fold.ctxt;

    {
        args: merge_arg_tys(srv, doc.id, doc.args),
        return: merge_ret_ty(srv, doc.id, doc.return),
        sig: get_fn_sig(srv, doc.id)
        with doc
    }
}

fn get_fn_sig(srv: astsrv::srv, fn_id: doc::ast_id) -> option<str> {
    astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(fn_id) {
          ast_map::node_item(@{
            ident: ident,
            node: ast::item_fn(decl, _, blk), _
          }) {
            some(pprust::fun_to_str(decl, ident, []))
          }
        }
    }
}

#[test]
fn should_add_fn_sig() {
    let source = "fn a() -> int { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.fns()[0].sig == some("fn a() -> int");
}

fn merge_ret_ty(
    srv: astsrv::srv,
    fn_id: doc::ast_id,
    doc: doc::retdoc
) -> doc::retdoc {
    alt get_ret_ty(srv, fn_id) {
      some(ty) {
        {
            ty: some(ty)
            with doc
        }
      }
      none { doc }
    }
}

fn get_ret_ty(srv: astsrv::srv, fn_id: doc::ast_id) -> option<str> {
    astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(fn_id) {
          ast_map::node_item(@{
            node: ast::item_fn(decl, _, _), _
          }) {
            if decl.output.node != ast::ty_nil {
                some(pprust::ty_to_str(decl.output))
            } else {
                // Nil-typed return values are not interesting
                none
            }
          }
        }
    }
}

#[test]
fn should_add_fn_ret_types() {
    let source = "fn a() -> int { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.fns()[0].return.ty == some("int");
}

#[test]
fn should_not_add_nil_ret_type() {
    let source = "fn a() { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.fns()[0].return.ty == none;
}

fn merge_arg_tys(
    srv: astsrv::srv,
    fn_id: doc::ast_id,
    args: [doc::argdoc]
) -> [doc::argdoc] {
    let tys = get_arg_tys(srv, fn_id);
    vec::map2(args, tys) {|arg, ty|
        // Sanity check that we're talking about the same args
        assert arg.name == tuple::first(ty);
        {
            ty: some(tuple::second(ty))
            with arg
        }
    }
}

fn get_arg_tys(srv: astsrv::srv, fn_id: doc::ast_id) -> [(str, str)] {
    astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(fn_id) {
          ast_map::node_item(@{
            node: ast::item_fn(decl, _, _), _
          }) |
          ast_map::node_item(@{
            node: ast::item_res(decl, _, _, _, _), _
          }) {
            vec::map(decl.inputs) {|arg|
                (arg.ident, pprust::ty_to_str(arg.ty))
            }
          }
        }
    }
}

#[test]
fn should_add_arg_types() {
    let source = "fn a(b: int, c: bool) { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    let fn_ = doc.topmod.fns()[0];
    assert fn_.args[0].ty == some("int");
    assert fn_.args[1].ty == some("bool");
}

fn fold_const(
    fold: fold::fold<astsrv::srv>,
    doc: doc::constdoc
) -> doc::constdoc {
    let srv = fold.ctxt;

    {
        ty: some(astsrv::exec(srv) {|ctxt|
            alt ctxt.ast_map.get(doc.id) {
              ast_map::node_item(@{
                node: ast::item_const(ty, _), _
              }) {
                pprust::ty_to_str(ty)
              }
            }
        })
        with doc
    }
}

#[test]
fn should_add_const_types() {
    let source = "const a: bool = true;";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.consts()[0].ty == some("bool");
}

fn fold_enum(
    fold: fold::fold<astsrv::srv>,
    doc: doc::enumdoc
) -> doc::enumdoc {
    let srv = fold.ctxt;

    {
        variants: vec::map(doc.variants) {|variant|
            let sig = astsrv::exec(srv) {|ctxt|
                alt ctxt.ast_map.get(doc.id) {
                  ast_map::node_item(@{
                    node: ast::item_enum(ast_variants, _), _
                  }) {
                    let ast_variant = option::get(
                        vec::find(ast_variants) {|v|
                            v.node.name == variant.name
                        });

                    pprust::variant_to_str(ast_variant)
                  }
                }
            };

            {
                sig: some(sig)
                with variant
            }
        }
        with doc
    }
}

#[test]
fn should_add_variant_sigs() {
    let source = "enum a { b(int) }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.enums()[0].variants[0].sig == some("b(int)");
}

fn fold_res(
    fold: fold::fold<astsrv::srv>,
    doc: doc::resdoc
) -> doc::resdoc {
    let srv = fold.ctxt;

    {
        args: merge_arg_tys(srv, doc.id, doc.args),
        sig: some(astsrv::exec(srv) {|ctxt|
            alt ctxt.ast_map.get(doc.id) {
              ast_map::node_item(@{
                node: ast::item_res(decl, _, _, _, _), _
              }) {
                pprust::res_to_str(decl, doc.name, [])
              }
            }
        })
        with doc
    }
}

#[test]
fn should_add_resource_sigs() {
    let source = "resource r(b: bool) { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.resources()[0].sig == some("resource r(b: bool)");
}

#[test]
fn should_add_resource_arg_tys() {
    let source = "resource r(a: bool) { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.resources()[0].args[0].ty == some("bool");
}<commit_msg>rustdoc: Add iface method type info to the doc tree<commit_after>#[doc =
  "Pulls type information out of the AST and attaches it to the document"];

import rustc::syntax::ast;
import rustc::syntax::print::pprust;
import rustc::middle::ast_map;

export mk_pass;

fn mk_pass() -> pass {
    run
}

fn run(
    srv: astsrv::srv,
    doc: doc::cratedoc
) -> doc::cratedoc {
    let fold = fold::fold({
        fold_fn: fold_fn,
        fold_const: fold_const,
        fold_enum: fold_enum,
        fold_res: fold_res,
        fold_iface: fold_iface
        with *fold::default_seq_fold(srv)
    });
    fold.fold_crate(fold, doc)
}

fn fold_fn(
    fold: fold::fold<astsrv::srv>,
    doc: doc::fndoc
) -> doc::fndoc {

    let srv = fold.ctxt;

    {
        args: merge_arg_tys(srv, doc.id, doc.args),
        return: merge_ret_ty(srv, doc.id, doc.return),
        sig: get_fn_sig(srv, doc.id)
        with doc
    }
}

fn get_fn_sig(srv: astsrv::srv, fn_id: doc::ast_id) -> option<str> {
    astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(fn_id) {
          ast_map::node_item(@{
            ident: ident,
            node: ast::item_fn(decl, _, blk), _
          }) {
            some(pprust::fun_to_str(decl, ident, []))
          }
        }
    }
}

#[test]
fn should_add_fn_sig() {
    let source = "fn a() -> int { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.fns()[0].sig == some("fn a() -> int");
}

fn merge_ret_ty(
    srv: astsrv::srv,
    fn_id: doc::ast_id,
    doc: doc::retdoc
) -> doc::retdoc {
    alt get_ret_ty(srv, fn_id) {
      some(ty) {
        {
            ty: some(ty)
            with doc
        }
      }
      none { doc }
    }
}

fn get_ret_ty(srv: astsrv::srv, fn_id: doc::ast_id) -> option<str> {
    astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(fn_id) {
          ast_map::node_item(@{
            node: ast::item_fn(decl, _, _), _
          }) {
            ret_ty_to_str(decl)
          }
        }
    }
}

fn ret_ty_to_str(decl: ast::fn_decl) -> option<str> {
    if decl.output.node != ast::ty_nil {
        some(pprust::ty_to_str(decl.output))
    } else {
        // Nil-typed return values are not interesting
        none
    }
}

#[test]
fn should_add_fn_ret_types() {
    let source = "fn a() -> int { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.fns()[0].return.ty == some("int");
}

#[test]
fn should_not_add_nil_ret_type() {
    let source = "fn a() { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.fns()[0].return.ty == none;
}

fn merge_arg_tys(
    srv: astsrv::srv,
    fn_id: doc::ast_id,
    args: [doc::argdoc]
) -> [doc::argdoc] {
    let tys = get_arg_tys(srv, fn_id);
    vec::map2(args, tys) {|arg, ty|
        // Sanity check that we're talking about the same args
        assert arg.name == tuple::first(ty);
        {
            ty: some(tuple::second(ty))
            with arg
        }
    }
}

fn get_arg_tys(srv: astsrv::srv, fn_id: doc::ast_id) -> [(str, str)] {
    astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(fn_id) {
          ast_map::node_item(@{
            node: ast::item_fn(decl, _, _), _
          }) |
          ast_map::node_item(@{
            node: ast::item_res(decl, _, _, _, _), _
          }) {
            decl_arg_tys(decl)
          }
        }
    }
}

fn decl_arg_tys(decl: ast::fn_decl) -> [(str, str)] {
    vec::map(decl.inputs) {|arg|
        (arg.ident, pprust::ty_to_str(arg.ty))
    }
}

#[test]
fn should_add_arg_types() {
    let source = "fn a(b: int, c: bool) { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    let fn_ = doc.topmod.fns()[0];
    assert fn_.args[0].ty == some("int");
    assert fn_.args[1].ty == some("bool");
}

fn fold_const(
    fold: fold::fold<astsrv::srv>,
    doc: doc::constdoc
) -> doc::constdoc {
    let srv = fold.ctxt;

    {
        ty: some(astsrv::exec(srv) {|ctxt|
            alt ctxt.ast_map.get(doc.id) {
              ast_map::node_item(@{
                node: ast::item_const(ty, _), _
              }) {
                pprust::ty_to_str(ty)
              }
            }
        })
        with doc
    }
}

#[test]
fn should_add_const_types() {
    let source = "const a: bool = true;";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.consts()[0].ty == some("bool");
}

fn fold_enum(
    fold: fold::fold<astsrv::srv>,
    doc: doc::enumdoc
) -> doc::enumdoc {
    let srv = fold.ctxt;

    {
        variants: vec::map(doc.variants) {|variant|
            let sig = astsrv::exec(srv) {|ctxt|
                alt ctxt.ast_map.get(doc.id) {
                  ast_map::node_item(@{
                    node: ast::item_enum(ast_variants, _), _
                  }) {
                    let ast_variant = option::get(
                        vec::find(ast_variants) {|v|
                            v.node.name == variant.name
                        });

                    pprust::variant_to_str(ast_variant)
                  }
                }
            };

            {
                sig: some(sig)
                with variant
            }
        }
        with doc
    }
}

#[test]
fn should_add_variant_sigs() {
    let source = "enum a { b(int) }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.enums()[0].variants[0].sig == some("b(int)");
}

fn fold_res(
    fold: fold::fold<astsrv::srv>,
    doc: doc::resdoc
) -> doc::resdoc {
    let srv = fold.ctxt;

    {
        args: merge_arg_tys(srv, doc.id, doc.args),
        sig: some(astsrv::exec(srv) {|ctxt|
            alt ctxt.ast_map.get(doc.id) {
              ast_map::node_item(@{
                node: ast::item_res(decl, _, _, _, _), _
              }) {
                pprust::res_to_str(decl, doc.name, [])
              }
            }
        })
        with doc
    }
}

#[test]
fn should_add_resource_sigs() {
    let source = "resource r(b: bool) { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.resources()[0].sig == some("resource r(b: bool)");
}

#[test]
fn should_add_resource_arg_tys() {
    let source = "resource r(a: bool) { }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.resources()[0].args[0].ty == some("bool");
}

fn fold_iface(
    fold: fold::fold<astsrv::srv>,
    doc: doc::ifacedoc
) -> doc::ifacedoc {

    let srv = fold.ctxt;

    {
        methods: vec::map(doc.methods) {|methoddoc|
            {
                args: merge_method_arg_tys(
                    srv,
                    doc.id,
                    methoddoc.args,
                    methoddoc.name),
                return: merge_method_ret_ty(
                    srv,
                    doc.id,
                    methoddoc.return,
                    methoddoc.name),
                sig: get_method_sig(srv, doc.id, methoddoc.name)
                with methoddoc
            }
        }
        with doc
    }
}

fn merge_method_ret_ty(
    srv: astsrv::srv,
    item_id: doc::ast_id,
    doc: doc::retdoc,
    method_name: str
) -> doc::retdoc {
    alt get_method_ret_ty(srv, item_id, method_name) {
      some(ty) {
        {
            ty: some(ty)
            with doc
        }
      }
      none { doc }
    }
}

fn get_method_ret_ty(
    srv: astsrv::srv,
    item_id: doc::ast_id,
    method_name: str
) -> option<str> {
    astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(item_id) {
          ast_map::node_item(@{
            node: ast::item_iface(_, methods), _
          }) {
            alt vec::find(methods) {|method|
                method.ident == method_name
            } {
                some(method) {
                    ret_ty_to_str(method.decl)
                }
            }
          }
        }
    }
}

fn get_method_sig(
    srv: astsrv::srv,
    item_id: doc::ast_id,
    method_name: str
) -> option<str> {
    astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(item_id) {
          ast_map::node_item(@{
            node: ast::item_iface(_, methods), _
          }) {
            alt vec::find(methods) {|method|
                method.ident == method_name
            } {
                some(method) {
                    some(pprust::fun_to_str(method.decl, method.ident, []))
                }
            }
          }
        }
    }
}

fn merge_method_arg_tys(
    srv: astsrv::srv,
    item_id: doc::ast_id,
    args: [doc::argdoc],
    method_name: str
) -> [doc::argdoc] {
    let tys = get_method_arg_tys(srv, item_id, method_name);
    vec::map2(args, tys) {|arg, ty|
        assert arg.name == tuple::first(ty);
        {
            ty: some(tuple::second(ty))
            with arg
        }
    }
}

fn get_method_arg_tys(
    srv: astsrv::srv,
    item_id: doc::ast_id,
    method_name: str
) -> [(str, str)] {
    astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(item_id) {
          ast_map::node_item(@{
            node: ast::item_iface(_, methods), _
          }) {
            alt vec::find(methods) {|method|
                method.ident == method_name
            } {
                some(method) {
                    decl_arg_tys(method.decl)
                }
            }
          }
        }
    }
}

#[test]
fn should_add_iface_method_sigs() {
    let source = "iface i { fn a() -> int; }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.ifaces()[0].methods[0].sig == some("fn a() -> int");
}

#[test]
fn should_add_iface_method_ret_types() {
    let source = "iface i { fn a() -> int; }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.ifaces()[0].methods[0].return.ty == some("int");
}

#[test]
fn should_not_add_iface_method_nil_ret_type() {
    let source = "iface i { fn a(); }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    assert doc.topmod.ifaces()[0].methods[0].return.ty == none;
}

#[test]
fn should_add_iface_method_arg_types() {
    let source = "iface i { fn a(b: int, c: bool); }";
    let srv = astsrv::mk_srv_from_str(source);
    let doc = extract::from_srv(srv, "");
    let doc = run(srv, doc);
    let fn_ = doc.topmod.ifaces()[0].methods[0];
    assert fn_.args[0].ty == some("int");
    assert fn_.args[1].ty == some("bool");
}
<|endoftext|>


<commit_before>const LARGE_MASTER_SECRET: i32 = 256;
const LARGE_PRIME: i32 = 1024;
const LARGE_VPRIME: i32 = 2128;
const LARGE_E_START: i32 = 596;
const LARGE_E_END_RANGE: i32 = 119;
extern crate openssl;
extern crate int_traits;
use self::int_traits::IntTraits;
use self::openssl::bn::{BigNum, BigNumRef, BigNumContext, MSB_MAYBE_ZERO};
use self::openssl::hash::{hash, MessageDigest};
pub struct AnoncredsService {
    dummy: String
}

impl AnoncredsService {
    pub fn new() -> AnoncredsService {
        trace!(target: "AnoncredsService", "new");
        AnoncredsService { dummy: "anoncreds_dummy".to_string() }
    }
    fn generate_master_secret() -> BigNum {
        let mut ms = BigNum::new().unwrap();
        ms.rand(LARGE_MASTER_SECRET, MSB_MAYBE_ZERO, false);
        ms
    }
    fn generate_public_key() -> PublicKey {
        //let (p_prime, q_prime) = (AnoncredsService::generate_prime(), AnoncredsService::generate_prime());
        let p_prime = BigNum::from_dec_str("147210949676505370022291901638323344651935110597590993130806944871698104433042968489453214046274983960765508724336420649095413993801340223096499490318385863961435462627523137938481776395548210420546733337321351064531462114552738775282293300556323029911674068388889455348206728016707243243859948314986927502343").unwrap();
        let q_prime = BigNum::from_dec_str("135780746061008989066681842882411968289578365330121870655195830818464118363874946689390282395824911410416094765498522070170715656164604448597511036312331994824492100665472180363433381994083327828179950784236529457340933711810709515143629906739084420423785456874473704622664344722021987863690561674302204741259").unwrap();
        //println!("p: {}\nq: {}", p_prime, q_prime);

        let (mut p, mut q, mut ctx, mut n, mut s, mut rms) = (
            BigNum::new().unwrap(),
            BigNum::new().unwrap(),
            BigNumContext::new().unwrap(),
            BigNum::new().unwrap(),
            BigNum::new().unwrap(),
            BigNum::new().unwrap()
        );

        p.checked_mul(&p_prime, &BigNum::from_u32(2).unwrap(), &mut ctx);
        p.add_word(1);
        q.checked_mul(&q_prime, &BigNum::from_u32(2).unwrap(), &mut ctx);
        q.add_word(1);
        //println!("p: {}\nq: {}", p, q);

        let mut n = BigNum::new().unwrap();
        n.checked_mul(&p, &q, &mut ctx);
        //println!("n: {}", n);

        s = AnoncredsService::random_qr(&n);
        //println!("s: {}", s);

        rms.mod_exp(&s, &AnoncredsService::gen_x(&p_prime, &q_prime), &n, &mut ctx);
        //println!("rms: {}", rms);

        PublicKey {n: n, s: s, rms: rms}
    }
    fn gen_x(p: &BigNum, q: &BigNum) -> BigNum {
        let mut ctx = BigNumContext::new().unwrap();
        let mut value = BigNum::new().unwrap();
        let mut result = BigNum::new().unwrap();

        value.checked_mul(&p, &q, &mut ctx);
        value.sub_word(3);

        result = AnoncredsService::random_in_range(&BigNum::from_u32(0).unwrap(), &value);
        result.add_word(2);
        result
    }
    fn gen_u(public_key: PublicKey, ms: BigNum) -> BigNum {
        let mut ctx = BigNumContext::new().unwrap();
        let mut vprime = BigNum::new().unwrap();
        vprime.rand(LARGE_VPRIME, MSB_MAYBE_ZERO, false);

        let mut result_mul_one = BigNum::new().unwrap();
        result_mul_one.mod_exp(&public_key.s, &vprime, &public_key.n, &mut ctx);

        let mut result_mul_two = BigNum::new().unwrap();
        result_mul_two.mod_exp(&public_key.rms, &ms, &public_key.n, &mut ctx);

        let mut u = &result_mul_one * &result_mul_two;
        u = &u % &public_key.n;
        u
    }
    fn random_in_range(start: &BigNum, end: &BigNum) -> BigNum {
        let mut random_number = BigNum::new().unwrap();
        let sub = end - start;

        random_number.rand(sub.num_bits(), MSB_MAYBE_ZERO, false).unwrap();
        while (&random_number + start) > *end {
            random_number.rand(sub.num_bits(), MSB_MAYBE_ZERO, false).unwrap();
        }

        &random_number + start
    }
    fn random_qr(n: &BigNum) -> BigNum {
        let (mut ctx, mut random_qr) = (BigNumContext::new().unwrap(), BigNum::new().unwrap());
        random_qr.sqr(&AnoncredsService::random_in_range(&BigNum::from_u32(0).unwrap(), &n), &mut ctx);
        random_qr
    }
    fn count_rounds_for_prime_check(prime: &BigNum) -> i32 {
        let prime_len = prime.to_dec_str().unwrap().len();
        prime_len.log2() as i32
    }
    fn generate_prime() -> BigNum {
        let mut ctx = BigNumContext::new().unwrap();
        let mut prime = BigNum::new().unwrap();
        let (mut is_prime, mut iteration) = (false, 0);

        while !is_prime {
            iteration += 1;
            prime.generate_prime(LARGE_PRIME, false, None, None);
            let checks = AnoncredsService::count_rounds_for_prime_check(&prime);
            let mut prime_for_check = BigNum::new().unwrap();
            prime_for_check.checked_mul(&prime, &BigNum::from_u32(2).unwrap(), &mut ctx).unwrap();
            prime_for_check.add_word(1);
            is_prime = prime_for_check.is_prime(checks, &mut ctx).unwrap();
            println!("Iteration: {}\nFound prime: {}\nis_prime: {}\n", iteration, prime, is_prime);
        }

        //println!("Generated prime: {}", prime);
        prime
    }
    fn generate_prime_in_range(start: &BigNum, end: &BigNum) -> Result<BigNum, &'static str>{
        let (mut iteration, max_iterations, mut prime_is_found, mut prime, mut ctx) = (
            0,
            100000,
            false,
            BigNum::new().unwrap(),
            BigNumContext::new().unwrap()
        );

        while (iteration < max_iterations) && !prime_is_found {
            prime = AnoncredsService::random_in_range(&start, &end);
            let checks = AnoncredsService::count_rounds_for_prime_check(&prime);

            if prime.is_prime(checks, &mut ctx).unwrap() {
                prime_is_found = true;
                println!("Found prime in {} iteration", iteration);
            }
            iteration += 1;
        }

        if !prime_is_found {
            println!("Cannot find prime in {} iterations", max_iterations);
            Err("Prime is not found")
        } else {
            Ok(prime)
        }
    }
    fn create_claim_request() -> ClaimRequest {
        let public_key = AnoncredsService::generate_public_key();
        let master_secret = AnoncredsService::generate_master_secret();
        let u = AnoncredsService::gen_u(public_key, master_secret);
        let claim_request = ClaimRequest {u: u};
        claim_request
    }
    fn issue_primary_claim(attributes: &Vec<AttributeType>, u: &BigNum) {
        let mut ctx = BigNumContext::new().unwrap();
        println!("{}\n{:?}", u, attributes);
        //calc vprimeprime
        let (mut e_start, mut e_end) = (BigNum::new().unwrap(), BigNum::new().unwrap());
        e_start.exp(&BigNum::from_u32(2).unwrap(), &BigNum::from_u32(LARGE_E_START as u32).unwrap(), &mut ctx);
        e_end.exp(&BigNum::from_u32(2).unwrap(), &BigNum::from_u32(LARGE_E_END_RANGE as u32).unwrap(), &mut ctx);
        e_end = &e_start + &e_end;
        let e = AnoncredsService::generate_prime_in_range(&e_start, &e_end).unwrap();
    }
    fn transform_byte_array_to_big_integer(vec: &Vec<u8>) -> BigNum {
        let mut ctx = BigNumContext::new().unwrap();
        let mut result = BigNum::from_u32(0).unwrap();
        for i in (0..vec.len()).rev() {
            let mut pow256 = BigNum::new().unwrap();
            pow256.exp(&BigNum::from_u32(256).unwrap(), &BigNum::from_u32(i as u32).unwrap(), &mut ctx);
            pow256 = &pow256 * &BigNum::from_u32(vec[vec.len() - 1 - i] as u32).unwrap();
            result = &result + &pow256;
        }
        result
    }
    fn encode_attribute(attribute: &str) -> BigNum {
        let mut result = hash(MessageDigest::sha256(), attribute.as_bytes()).unwrap();
        let index = result.iter().position(|&value| value == 0);
        let encoded_attribute = match index {
            Some(index) => {
                result.truncate(index);
                AnoncredsService::transform_byte_array_to_big_integer(&result)
            },
            None => {
                AnoncredsService::transform_byte_array_to_big_integer(&result)
            }
        };
        encoded_attribute
    }
}

#[derive(Debug)]
struct PublicKey {
    n: BigNum,
    s: BigNum,
    rms: BigNum
}

#[derive(Debug)]
struct ClaimRequest {
    u: BigNum
}

#[derive(Debug)]
struct AttributeType {
    name: String,
    encode: bool
}

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

    #[test]
    fn service_creation_is_possible() {
        let anoncreds_service = AnoncredsService::new();
        assert_eq!("anoncreds_dummy", anoncreds_service.dummy, "Dummy field is filled by constructor");
    }
    #[test]
    fn master_secret_generator_works() {
        let ms: BigNum = AnoncredsService::generate_master_secret();
        assert_eq!(LARGE_MASTER_SECRET/8, ms.num_bytes());
    }
    #[test]
    fn random_in_range_works() {
        let (mut start, mut end) = (BigNum::new().unwrap(), BigNum::new().unwrap());

        start.rand(LARGE_PRIME, MSB_MAYBE_ZERO, false).unwrap();
        end.rand(LARGE_PRIME, MSB_MAYBE_ZERO, false).unwrap();

        while end < start {
            end.rand(LARGE_PRIME, MSB_MAYBE_ZERO, false).unwrap();
        }

        let random_in_range = AnoncredsService::random_in_range(&start, &end);
        assert!((random_in_range > start) && (random_in_range < end));
    }
//    #[test]
//    fn generate_prime_works() {
//        let prime = AnoncredsService::generate_prime();
//        let mut ctx = BigNumContext::new().unwrap();
//        let checks = AnoncredsService::count_rounds_for_prime_check(&prime);
//        let is_prime = prime.is_prime(checks, &mut ctx).unwrap();
//        assert_eq!(is_prime, true);
//    }
    #[test]
    fn encode_attribute_works() {
        let test_str_one = "Alexer5435";
        let test_str_two = "Alexer";
        let test_answer_one = BigNum::from_dec_str("62794").unwrap();
        let test_answer_two = BigNum::from_dec_str("93838255634171043313693932530283701522875554780708470423762684802192372035729").unwrap();
        assert_eq!(test_answer_one, AnoncredsService::encode_attribute(test_str_one));
        assert_eq!(test_answer_two, AnoncredsService::encode_attribute(test_str_two));
    }
    #[test]
    fn anoncreds_works() {
        let mut attributes = vec![
            AttributeType {name: "name".to_string(), encode: true},
            AttributeType {name: "age".to_string(), encode: false},
            AttributeType {name: "height".to_string(), encode: false},
            AttributeType {name: "sex".to_string(), encode: true}
        ];
        let claim_request = AnoncredsService::create_claim_request();
        let claim = AnoncredsService::issue_primary_claim(&attributes, &claim_request.u);
        let encoded_attribute = AnoncredsService::encode_attribute("Alexer");
    }
}<commit_msg>Added bitwise_or function for big ints<commit_after>const LARGE_MASTER_SECRET: i32 = 256;
const LARGE_PRIME: i32 = 1024;
const LARGE_VPRIME: i32 = 2128;
const LARGE_E_START: i32 = 596;
const LARGE_E_END_RANGE: i32 = 119;
extern crate openssl;
extern crate int_traits;
use self::int_traits::IntTraits;
use self::openssl::bn::{BigNum, BigNumRef, BigNumContext, MSB_MAYBE_ZERO};
use self::openssl::hash::{hash, MessageDigest};
use std::cmp::max;
pub struct AnoncredsService {
    dummy: String
}

impl AnoncredsService {
    pub fn new() -> AnoncredsService {
        trace!(target: "AnoncredsService", "new");
        AnoncredsService { dummy: "anoncreds_dummy".to_string() }
    }
    fn generate_master_secret() -> BigNum {
        let mut ms = BigNum::new().unwrap();
        ms.rand(LARGE_MASTER_SECRET, MSB_MAYBE_ZERO, false);
        ms
    }
    fn generate_public_key() -> PublicKey {
        //let (p_prime, q_prime) = (AnoncredsService::generate_prime(), AnoncredsService::generate_prime());
        let p_prime = BigNum::from_dec_str("147210949676505370022291901638323344651935110597590993130806944871698104433042968489453214046274983960765508724336420649095413993801340223096499490318385863961435462627523137938481776395548210420546733337321351064531462114552738775282293300556323029911674068388889455348206728016707243243859948314986927502343").unwrap();
        let q_prime = BigNum::from_dec_str("135780746061008989066681842882411968289578365330121870655195830818464118363874946689390282395824911410416094765498522070170715656164604448597511036312331994824492100665472180363433381994083327828179950784236529457340933711810709515143629906739084420423785456874473704622664344722021987863690561674302204741259").unwrap();
        //println!("p: {}\nq: {}", p_prime, q_prime);

        let (mut p, mut q, mut ctx, mut n, mut s, mut rms) = (
            BigNum::new().unwrap(),
            BigNum::new().unwrap(),
            BigNumContext::new().unwrap(),
            BigNum::new().unwrap(),
            BigNum::new().unwrap(),
            BigNum::new().unwrap()
        );

        p.checked_mul(&p_prime, &BigNum::from_u32(2).unwrap(), &mut ctx);
        p.add_word(1);
        q.checked_mul(&q_prime, &BigNum::from_u32(2).unwrap(), &mut ctx);
        q.add_word(1);
        //println!("p: {}\nq: {}", p, q);

        let mut n = BigNum::new().unwrap();
        n.checked_mul(&p, &q, &mut ctx);
        //println!("n: {}", n);

        s = AnoncredsService::random_qr(&n);
        //println!("s: {}", s);

        rms.mod_exp(&s, &AnoncredsService::gen_x(&p_prime, &q_prime), &n, &mut ctx);
        //println!("rms: {}", rms);

        PublicKey {n: n, s: s, rms: rms}
    }
    fn gen_x(p: &BigNum, q: &BigNum) -> BigNum {
        let mut ctx = BigNumContext::new().unwrap();
        let mut value = BigNum::new().unwrap();
        let mut result = BigNum::new().unwrap();

        value.checked_mul(&p, &q, &mut ctx);
        value.sub_word(3);

        result = AnoncredsService::random_in_range(&BigNum::from_u32(0).unwrap(), &value);
        result.add_word(2);
        result
    }
    fn gen_u(public_key: PublicKey, ms: BigNum) -> BigNum {
        let mut ctx = BigNumContext::new().unwrap();
        let mut vprime = BigNum::new().unwrap();
        vprime.rand(LARGE_VPRIME, MSB_MAYBE_ZERO, false);

        let mut result_mul_one = BigNum::new().unwrap();
        result_mul_one.mod_exp(&public_key.s, &vprime, &public_key.n, &mut ctx);

        let mut result_mul_two = BigNum::new().unwrap();
        result_mul_two.mod_exp(&public_key.rms, &ms, &public_key.n, &mut ctx);

        let mut u = &result_mul_one * &result_mul_two;
        u = &u % &public_key.n;
        u
    }
    fn random_in_range(start: &BigNum, end: &BigNum) -> BigNum {
        let mut random_number = BigNum::new().unwrap();
        let sub = end - start;

        random_number.rand(sub.num_bits(), MSB_MAYBE_ZERO, false).unwrap();
        while (&random_number + start) > *end {
            random_number.rand(sub.num_bits(), MSB_MAYBE_ZERO, false).unwrap();
        }

        &random_number + start
    }
    fn random_qr(n: &BigNum) -> BigNum {
        let (mut ctx, mut random_qr) = (BigNumContext::new().unwrap(), BigNum::new().unwrap());
        random_qr.sqr(&AnoncredsService::random_in_range(&BigNum::from_u32(0).unwrap(), &n), &mut ctx);
        random_qr
    }
    fn count_rounds_for_prime_check(prime: &BigNum) -> i32 {
        let prime_len = prime.to_dec_str().unwrap().len();
        prime_len.log2() as i32
    }
    fn generate_prime() -> BigNum {
        let mut ctx = BigNumContext::new().unwrap();
        let mut prime = BigNum::new().unwrap();
        let (mut is_prime, mut iteration) = (false, 0);

        while !is_prime {
            iteration += 1;
            prime.generate_prime(LARGE_PRIME, false, None, None);
            let checks = AnoncredsService::count_rounds_for_prime_check(&prime);
            let mut prime_for_check = BigNum::new().unwrap();
            prime_for_check.checked_mul(&prime, &BigNum::from_u32(2).unwrap(), &mut ctx).unwrap();
            prime_for_check.add_word(1);
            is_prime = prime_for_check.is_prime(checks, &mut ctx).unwrap();
            println!("Iteration: {}\nFound prime: {}\nis_prime: {}\n", iteration, prime, is_prime);
        }

        //println!("Generated prime: {}", prime);
        prime
    }
    fn generate_prime_in_range(start: &BigNum, end: &BigNum) -> Result<BigNum, &'static str>{
        let (mut iteration, max_iterations, mut prime_is_found, mut prime, mut ctx) = (
            0,
            100000,
            false,
            BigNum::new().unwrap(),
            BigNumContext::new().unwrap()
        );

        while (iteration < max_iterations) && !prime_is_found {
            prime = AnoncredsService::random_in_range(&start, &end);
            let checks = AnoncredsService::count_rounds_for_prime_check(&prime);

            if prime.is_prime(checks, &mut ctx).unwrap() {
                prime_is_found = true;
                println!("Found prime in {} iteration", iteration);
            }
            iteration += 1;
        }

        if !prime_is_found {
            println!("Cannot find prime in {} iterations", max_iterations);
            Err("Prime is not found")
        } else {
            Ok(prime)
        }
    }
    fn create_claim_request() -> ClaimRequest {
        let public_key = AnoncredsService::generate_public_key();
        let master_secret = AnoncredsService::generate_master_secret();
        let u = AnoncredsService::gen_u(public_key, master_secret);
        let claim_request = ClaimRequest {u: u};
        claim_request
    }
    fn issue_primary_claim(attributes: &Vec<AttributeType>, u: &BigNum) {
        let mut ctx = BigNumContext::new().unwrap();
        println!("{}\n{:?}", u, attributes);
        //calc vprimeprime
        let (mut e_start, mut e_end) = (BigNum::new().unwrap(), BigNum::new().unwrap());
        e_start.exp(&BigNum::from_u32(2).unwrap(), &BigNum::from_u32(LARGE_E_START as u32).unwrap(), &mut ctx);
        e_end.exp(&BigNum::from_u32(2).unwrap(), &BigNum::from_u32(LARGE_E_END_RANGE as u32).unwrap(), &mut ctx);
        e_end = &e_start + &e_end;
        let e = AnoncredsService::generate_prime_in_range(&e_start, &e_end).unwrap();
    }
    fn transform_byte_array_to_big_integer(vec: &Vec<u8>) -> BigNum {
        let mut ctx = BigNumContext::new().unwrap();
        let mut result = BigNum::from_u32(0).unwrap();
        for i in (0..vec.len()).rev() {
            let mut pow256 = BigNum::new().unwrap();
            pow256.exp(&BigNum::from_u32(256).unwrap(), &BigNum::from_u32(i as u32).unwrap(), &mut ctx);
            pow256 = &pow256 * &BigNum::from_u32(vec[vec.len() - 1 - i] as u32).unwrap();
            result = &result + &pow256;
        }
        result
    }
    fn encode_attribute(attribute: &str) -> BigNum {
        let mut result = hash(MessageDigest::sha256(), attribute.as_bytes()).unwrap();
        let index = result.iter().position(|&value| value == 0);
        let encoded_attribute = match index {
            Some(index) => {
                result.truncate(index);
                AnoncredsService::transform_byte_array_to_big_integer(&result)
            },
            None => {
                AnoncredsService::transform_byte_array_to_big_integer(&result)
            }
        };
        encoded_attribute
    }
    fn bitwise_or_big_int(a: &BigNum, b: &BigNum) -> BigNum {
        let significant_bits = max(a.num_bits(), b.num_bits());
        let mut result = BigNum::new().unwrap();
        for i in 0..significant_bits {
            if a.is_bit_set(i) || b.is_bit_set(i) {
                result.set_bit(i);
            }
        }
        result
    }
}

#[derive(Debug)]
struct PublicKey {
    n: BigNum,
    s: BigNum,
    rms: BigNum
}

#[derive(Debug)]
struct ClaimRequest {
    u: BigNum
}

#[derive(Debug)]
struct AttributeType {
    name: String,
    encode: bool
}

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

    #[test]
    fn service_creation_is_possible() {
        let anoncreds_service = AnoncredsService::new();
        assert_eq!("anoncreds_dummy", anoncreds_service.dummy, "Dummy field is filled by constructor");
    }
    #[test]
    fn master_secret_generator_works() {
        let ms: BigNum = AnoncredsService::generate_master_secret();
        assert_eq!(LARGE_MASTER_SECRET/8, ms.num_bytes());
    }
    #[test]
    fn random_in_range_works() {
        let (mut start, mut end) = (BigNum::new().unwrap(), BigNum::new().unwrap());

        start.rand(LARGE_PRIME, MSB_MAYBE_ZERO, false).unwrap();
        end.rand(LARGE_PRIME, MSB_MAYBE_ZERO, false).unwrap();

        while end < start {
            end.rand(LARGE_PRIME, MSB_MAYBE_ZERO, false).unwrap();
        }

        let random_in_range = AnoncredsService::random_in_range(&start, &end);
        assert!((random_in_range > start) && (random_in_range < end));
    }
//    #[test]
//    fn generate_prime_works() {
//        let prime = AnoncredsService::generate_prime();
//        let mut ctx = BigNumContext::new().unwrap();
//        let checks = AnoncredsService::count_rounds_for_prime_check(&prime);
//        let is_prime = prime.is_prime(checks, &mut ctx).unwrap();
//        assert_eq!(is_prime, true);
//    }
    #[test]
    fn encode_attribute_works() {
        let test_str_one = "Alexer5435";
        let test_str_two = "Alexer";
        let test_answer_one = BigNum::from_dec_str("62794").unwrap();
        let test_answer_two = BigNum::from_dec_str("93838255634171043313693932530283701522875554780708470423762684802192372035729").unwrap();
        assert_eq!(test_answer_one, AnoncredsService::encode_attribute(test_str_one));
        assert_eq!(test_answer_two, AnoncredsService::encode_attribute(test_str_two));
    }
    #[test]
    fn bitwise_or_big_int_works () {
        let a = BigNum::from_dec_str("778378032744961463933002553964902776831187587689736807008034459507677878432383414623740074").unwrap();
        let b = BigNum::from_dec_str("1018517988167243043134222844204689080525734196832968125318070224677190649881668353091698688").unwrap();
        let result = BigNum::from_dec_str("1796896020912204507067225398169591857356921784522704932326104684184868528314051767715438762").unwrap();
        assert_eq!(result, AnoncredsService::bitwise_or_big_int(&a, &b));
    }
    #[test]
    fn anoncreds_works() {
        let mut attributes = vec![
            AttributeType {name: "name".to_string(), encode: true},
            AttributeType {name: "age".to_string(), encode: false},
            AttributeType {name: "height".to_string(), encode: false},
            AttributeType {name: "sex".to_string(), encode: true}
        ];
        let claim_request = AnoncredsService::create_claim_request();
        let claim = AnoncredsService::issue_primary_claim(&attributes, &claim_request.u);
        let encoded_attribute = AnoncredsService::encode_attribute("Alexer");
    }
}<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::str::FromStr;
use std::collections::HashMap;

#[derive(Debug)]
enum Input {
    Value(isize),
    Register(char),
}

#[derive(Debug)]
enum Instruction {
    Cpy(Input, char),
    Inc(char),
    Dec(char),
    Jnz(Input, isize),
}

impl FromStr for Instruction {
    type Err = &'static str;
    fn from_str(input: &str) -> Result<Instruction, Self::Err> {
        let input = input.split_whitespace().collect::<Vec<_>>();
        use Instruction::*;
        let to_char = |text: &str| text.chars().next().unwrap();
        let get_input = |text: &str| match text.parse::<isize>() {
            Ok(v) => Input::Value(v),
            Err(_) => Input::Register(to_char(text)),
        };
        match input[0] {
            "cpy" => Ok(Cpy(get_input(input[1]), to_char(input[2]))),
            "inc" => Ok(Inc(to_char(input[1]))),
            "dec" => Ok(Dec(to_char(input[1]))),
            "jnz" => Ok(Jnz(get_input(input[1]), input[2].parse::<isize>().unwrap())),
            _ => Err("invalid instruction"),
        }
    }
}

// impl fmt::Display for Instruction {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         let out = match self {
//             Cpy
//         }
//         write!(f, "{}", )
//     }
// }

fn main() {
    let stdin = io::stdin();
    let stdin = stdin.lock().lines();

    let mut program = Vec::<Instruction>::new();

    for line in stdin {
        let line = line.unwrap();
        let instruction: Instruction = line.parse().unwrap();
        program.push(instruction);
    }

    let mut pc: isize = 0;
    let mut cpu = HashMap::<char, isize>::new();

    cpu.insert('a', 0);
    cpu.insert('b', 0);
    cpu.insert('c', 0);
    cpu.insert('d', 0);

    use Input::*;
    use Instruction::*;
    loop {
        match program[pc as usize] {
            Cpy(Value(x), reg) => {
                *cpu.get_mut(&reg).unwrap() = x;
                pc += 1;
            }
            Cpy(Register(reg1), reg2) => {
                *cpu.get_mut(&reg2).unwrap() = *cpu.get(&reg1).unwrap();
                pc += 1;
            }
            Inc(reg) => {
                *cpu.get_mut(&reg).unwrap() += 1;
                pc += 1;
            }
            Dec(reg) => {
                *cpu.get_mut(&reg).unwrap() -= 1;
                pc += 1;
            }
            Jnz(Value(x), offset) => if x != 0 { pc += offset } else { pc += 1 },
            Jnz(Register(reg), offset) => {
                if *cpu.get(&reg).unwrap() != 0 {
                    pc += offset
                } else {
                    pc += 1
                }
            }
        }

        if pc >= program.len() as isize {
            println!("part 1: {}", cpu[&'a']);
            break;
        }
    }
}
<commit_msg>rust day12 part 2<commit_after>use std::io;
use std::io::prelude::*;
use std::str::FromStr;
use std::collections::HashMap;

#[derive(Debug)]
enum Input {
    Value(isize),
    Register(char),
}

#[derive(Debug)]
enum Instruction {
    Cpy(Input, char),
    Inc(char),
    Dec(char),
    Jnz(Input, isize),
}

impl FromStr for Instruction {
    type Err = &'static str;
    fn from_str(input: &str) -> Result<Instruction, Self::Err> {
        let input = input.split_whitespace().collect::<Vec<_>>();
        use Instruction::*;
        let to_char = |text: &str| text.chars().next().unwrap();
        let get_input = |text: &str| match text.parse::<isize>() {
            Ok(v) => Input::Value(v),
            Err(_) => Input::Register(to_char(text)),
        };
        match input[0] {
            "cpy" => Ok(Cpy(get_input(input[1]), to_char(input[2]))),
            "inc" => Ok(Inc(to_char(input[1]))),
            "dec" => Ok(Dec(to_char(input[1]))),
            "jnz" => Ok(Jnz(get_input(input[1]), input[2].parse::<isize>().unwrap())),
            _ => Err("invalid instruction"),
        }
    }
}

fn run(cpu: &mut HashMap<char, isize>, program: &Vec<Instruction>) -> isize {
    use Input::*;
    use Instruction::*;

    let mut pc: isize = 0;

    loop {
        match program[pc as usize] {
            Cpy(Value(x), reg) => {
                *cpu.get_mut(&reg).unwrap() = x;
                pc += 1;
            }
            Cpy(Register(reg1), reg2) => {
                *cpu.get_mut(&reg2).unwrap() = cpu[&reg1];
                pc += 1;
            }
            Inc(reg) => {
                *cpu.get_mut(&reg).unwrap() += 1;
                pc += 1;
            }
            Dec(reg) => {
                *cpu.get_mut(&reg).unwrap() -= 1;
                pc += 1;
            }
            Jnz(Value(x), offset) => if x != 0 { pc += offset } else { pc += 1 },
            Jnz(Register(reg), offset) => {
                if cpu[&reg] != 0 {
                    pc += offset
                } else {
                    pc += 1
                }
            }
        }

        if pc >= program.len() as isize {
            return cpu[&'a'];
        }
    }
}

fn main() {
    let stdin = io::stdin();
    let stdin = stdin.lock().lines();

    let mut program = Vec::<Instruction>::new();

    for line in stdin {
        let line = line.unwrap();
        let instruction: Instruction = line.parse().unwrap();
        program.push(instruction);
    }
    let program = program;

    let mut cpu = HashMap::<char, isize>::new();

    cpu.insert('a', 0);
    cpu.insert('b', 0);
    cpu.insert('c', 0);
    cpu.insert('d', 0);
    let part1 = run(&mut cpu, &program);

    cpu.insert('a', 0);
    cpu.insert('b', 0);
    cpu.insert('c', 1);
    cpu.insert('d', 0);
    let part2 = run(&mut cpu, &program);

    println!("part 1: {}", part1);
    println!("part 2: {}", part2);
}
<|endoftext|>


<commit_before>extern crate libc;

use std::ffi::{CString, CStr};

#[link(name = "ncurses")]
extern "C" {
    fn getpid() -> i32;
    fn initscr() -> *mut i8;
    fn noecho() -> i32;
    fn curs_set(c: i8) -> i32;
    #[link_name = "box"]
    fn box_(window: *mut i8, verch: i8, horch: i8);
    fn mvprintw(y: i8, x: i8, s: *const libc::c_char);
    fn getch() -> *mut i8;
    fn endwin() -> libc::c_int;
}

trait ToCStr {
    fn to_c_str(&self) -> CString;
}

impl<'a> ToCStr for &'a str {
    fn to_c_str(&self) -> CString {
        CString::new(*self).unwrap()
    }
}

fn main() {
    unsafe {
        let window = initscr();
        noecho();
        curs_set(0);

        let mut c = 0;
        mvprintw(3, 2, "hello 22".to_c_str().as_ptr());
        box_(window, 0, 0);
        getch();

        endwin();
    }
}

#[derive(Debug, PartialEq, Clone)]
struct Snake {
    cells: Vec<Cell>,
    direction: Direction,
}

impl Snake {
    fn slide(&self) -> Snake {
        let mut vec: Vec<Cell> = vec![self.cells.first().unwrap().move_in(&self.direction)];

        let mut new_cells = self.cells.clone();
        new_cells.pop();
        vec.append(&mut new_cells);

        Snake { cells: vec, direction: self.direction }
    }
}

#[derive(Debug, PartialEq, Clone)]
struct Cell {
    x: i32,
    y: i32,
}

impl Cell {
    fn move_in(&self, direction: &Direction) -> Cell {
        let dx = match direction {
            Direction::Up => 0,
            Direction::Down => 0,
            Direction::Left => -1,
            Direction::Right => 1,
        };
        let dy = match direction {
            Direction::Up => -1,
            Direction::Down => 1,
            Direction::Left => 0,
            Direction::Right => 0,
        };
        return Cell { x: self.x + dx, y: self.y + dy };
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

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

    #[test]
    fn it_works() {
        let snake = Snake {
            cells: vec![Cell { x: 2, y: 0 }, Cell { x: 1, y: 0 }, Cell { x: 0, y: 0 }],
            direction: Direction::Right,
        };

        assert_eq!(
            snake.slide(),
            Snake {
                cells: vec![Cell { x: 3, y: 0 }, Cell { x: 2, y: 0 }, Cell { x: 1, y: 0 }],
                direction: Direction::Right,
            }
        )
    }
}<commit_msg>rust snake moving right<commit_after>extern crate libc;

use std::ffi::{CString, CStr};

#[link(name = "ncurses")]
extern "C" {
    fn getpid() -> i32;
    fn initscr() -> *mut i8;
    fn noecho() -> i32;
    fn curs_set(c: i8) -> i32;
    fn halfdelay(tenths: i8) -> i32;
    #[link_name = "box"]
    fn box_(window: *mut i8, verch: i8, horch: i8);
    fn mvprintw(y: i8, x: i8, s: *const libc::c_char);
    fn getch() -> i8;
    fn endwin() -> libc::c_int;

    fn newwin(nlines: i8, ncols: i8, begin_y: i8, begin_x: i8) -> *mut i8;
    fn wclear(window: *mut i8) -> i8;
    fn wrefresh(window: *mut i8) -> i8;
    fn mvwprintw(window: *mut i8, y: i8, x: i8, s: *const libc::c_char);
    fn wgetch(window: *mut i8) -> i8;
    fn delwin(window: *mut i8) -> i8;
}

trait ToCStr {
    fn to_c_str(&self) -> CString;
}

fn main() {
    unsafe {
        initscr();
        noecho();
        curs_set(0);
        halfdelay(3);

        let width = 20;
        let height = 10;
        let window = newwin(height + 2, width + 2, 0, 0);

        let mut snake = Snake {
            cells: vec![Cell { x: 2, y: 0 }, Cell { x: 1, y: 0 }, Cell { x: 0, y: 0 }],
            direction: Direction::Right,
        };

        let mut c = 0;
        while c != 'q' as i8 {
            wclear(window);
            box_(window, 0, 0);
            mvwprintw(window, snake.head().y + 1, snake.head().x + 1, "Q".to_c_str().as_ptr());
            snake.tail().iter().for_each(|cell|
                mvwprintw(window, cell.y + 1, cell.x + 1, "o".to_c_str().as_ptr())
            );

            wrefresh(window);

            c = wgetch(window);

            snake = snake.slide();
        }

        delwin(window);
        endwin();
    }
}

impl<'a> ToCStr for &'a str {
    fn to_c_str(&self) -> CString {
        CString::new(*self).unwrap()
    }
}

#[derive(Debug, PartialEq, Clone)]
struct Snake {
    cells: Vec<Cell>,
    direction: Direction,
}

impl Snake {
    fn head(&self) -> &Cell {
        &self.cells[0]
    }

    fn tail(&self) -> Vec<Cell> {
        self.cells[1..self.cells.len()].to_owned()
    }

    fn slide(&self) -> Snake {
        let mut vec: Vec<Cell> = vec![self.cells.first().unwrap().move_in(&self.direction)];

        let mut new_cells = self.cells.clone();
        new_cells.pop();
        vec.append(&mut new_cells);

        Snake { cells: vec, direction: self.direction }
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct Cell {
    x: i8,
    y: i8,
}

impl Cell {
    fn move_in(&self, direction: &Direction) -> Cell {
        let dx = match direction {
            Direction::Up => 0,
            Direction::Down => 0,
            Direction::Left => -1,
            Direction::Right => 1,
        };
        let dy = match direction {
            Direction::Up => -1,
            Direction::Down => 1,
            Direction::Left => 0,
            Direction::Right => 0,
        };
        return Cell { x: self.x + dx, y: self.y + dy };
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

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

    #[test]
    fn snake_moves_right() {
        let snake = Snake {
            cells: vec![Cell { x: 2, y: 0 }, Cell { x: 1, y: 0 }, Cell { x: 0, y: 0 }],
            direction: Direction::Right,
        };

        assert_eq!(
            snake.slide(),
            Snake {
                cells: vec![Cell { x: 3, y: 0 }, Cell { x: 2, y: 0 }, Cell { x: 1, y: 0 }],
                direction: Direction::Right,
            }
        )
    }
}<|endoftext|>


<commit_before>//! An implementation of Negamax.
//!
//! Currently, only the basic alpha-pruning variant is implemented. Further work
//! could add advanced features, like history and/or transposition tables. This
//! picks randomly among the "best" moves, so that it's non-deterministic.

use super::super::interface::*;
use rand;
use rand::Rng;
use std::cmp::max;
use std::marker::PhantomData;

/// Options to use for the `Negamax` engine.
pub struct Options {
    /// The maximum depth within the game tree.
    pub max_depth: usize,
}

pub struct Negamax<E> {
    opts: Options,
    rng: rand::ThreadRng,
    _eval: PhantomData<E>,
}

impl<E: Evaluator> Negamax<E> {
    pub fn new(opts: Options) -> Negamax<E> {
        Negamax {
            opts: opts,
            rng: rand::thread_rng(),
            _eval: PhantomData,
        }
    }

    fn negamax(&self,
               s: &mut <E::G as Game>::S,
               depth: usize,
               mut alpha: Evaluation,
               beta: Evaluation,
               p: Player)
               -> Evaluation
        where <<E as Evaluator>::G as Game>::M: Copy
    {
        let maybe_winner = E::G::get_winner(s);
        if depth == 0 || maybe_winner.is_some() {
            return p * E::evaluate(s, maybe_winner);
        }
        let mut moves = [None; 100];
        E::G::generate_moves(s, p, &mut moves);
        let mut best = Evaluation::Worst;
        for m in moves.iter().take_while(|om| om.is_some()).map(|om| om.unwrap()) {
            m.apply(s);
            let value = -self.negamax(s, depth - 1, -beta, -alpha, -p);
            m.undo(s);
            best = max(best, value);
            alpha = max(alpha, value);
            if alpha >= beta {
                break
            }
        }
        best
    }
}

impl<E: Evaluator> Strategy<E::G> for Negamax<E>
    where <E::G as Game>::S: Clone,
          <E::G as Game>::M: Copy {
    fn choose_move(&mut self, s: &<E::G as Game>::S, p: Player) -> Option<<E::G as Game>::M> {
        let mut best = Evaluation::Worst;
        let mut moves = [None; 100];
        E::G::generate_moves(s, p, &mut moves);
        let mut candidate_moves = Vec::new();
        let mut s_clone = s.clone();
        for m in moves.iter().take_while(|m| m.is_some()).map(|m| m.unwrap()) {
            // determine value for this move
            m.apply(&mut s_clone);
            let value = -self.negamax(&mut s_clone,
                                      self.opts.max_depth,
                                      Evaluation::Worst,
                                      Evaluation::Best,
                                      -p);
            m.undo(&mut s_clone);
            // this move is a candidate move
            if value == best {
                candidate_moves.push(m);
            // this move is better than any previous, so it's the sole candidate
            } else if value > best {
                candidate_moves.clear();
                candidate_moves.push(m);
                best = value;
            }
        }
        if candidate_moves.is_empty() {
            None
        } else {
            Some(candidate_moves[self.rng.gen_range(0, candidate_moves.len())])
        }
    }
}
<commit_msg>Make core Negamax function standalone.<commit_after>//! An implementation of Negamax.
//!
//! Currently, only the basic alpha-pruning variant is implemented. Further work
//! could add advanced features, like history and/or transposition tables. This
//! picks randomly among the "best" moves, so that it's non-deterministic.

use super::super::interface::*;
use rand;
use rand::Rng;
use std::cmp::max;
use std::marker::PhantomData;

fn negamax<E: Evaluator>(s: &mut <E::G as Game>::S,
                         depth: usize,
                         mut alpha: Evaluation,
                         beta: Evaluation,
                         p: Player)
                         -> Evaluation
    where <<E as Evaluator>::G as Game>::M: Copy
{
    let maybe_winner = E::G::get_winner(s);
    if depth == 0 || maybe_winner.is_some() {
        return p * E::evaluate(s, maybe_winner);
    }
    let mut moves = [None; 100];
    E::G::generate_moves(s, p, &mut moves);
    let mut best = Evaluation::Worst;
    for m in moves.iter().take_while(|om| om.is_some()).map(|om| om.unwrap()) {
        m.apply(s);
        let value = -negamax::<E>(s, depth - 1, -beta, -alpha, -p);
        m.undo(s);
        best = max(best, value);
        alpha = max(alpha, value);
        if alpha >= beta {
            break
        }
    }
    best
}

/// Options to use for the `Negamax` engine.
pub struct Options {
    /// The maximum depth within the game tree.
    pub max_depth: usize,
}

pub struct Negamax<E> {
    opts: Options,
    rng: rand::ThreadRng,
    _eval: PhantomData<E>,
}

impl<E: Evaluator> Negamax<E> {
    pub fn new(opts: Options) -> Negamax<E> {
        Negamax {
            opts: opts,
            rng: rand::thread_rng(),
            _eval: PhantomData,
        }
    }
}

impl<E: Evaluator> Strategy<E::G> for Negamax<E>
    where <E::G as Game>::S: Clone,
          <E::G as Game>::M: Copy {
    fn choose_move(&mut self, s: &<E::G as Game>::S, p: Player) -> Option<<E::G as Game>::M> {
        let mut best = Evaluation::Worst;
        let mut moves = [None; 100];
        E::G::generate_moves(s, p, &mut moves);
        let mut candidate_moves = Vec::new();
        let mut s_clone = s.clone();
        for m in moves.iter().take_while(|m| m.is_some()).map(|m| m.unwrap()) {
            // determine value for this move
            m.apply(&mut s_clone);
            let value = -negamax::<E>(&mut s_clone,
                                      self.opts.max_depth,
                                      Evaluation::Worst,
                                      Evaluation::Best,
                                      -p);
            m.undo(&mut s_clone);
            // this move is a candidate move
            if value == best {
                candidate_moves.push(m);
            // this move is better than any previous, so it's the sole candidate
            } else if value > best {
                candidate_moves.clear();
                candidate_moves.push(m);
                best = value;
            }
        }
        if candidate_moves.is_empty() {
            None
        } else {
            Some(candidate_moves[self.rng.gen_range(0, candidate_moves.len())])
        }
    }
}
<|endoftext|>