<commit_before>//! This module implements `LzmaWriter`.
//!
//! `LzmaWriter` implements the LZMA (XZ) compression/decompression algorithm as a generic
//! Writer.  In other words, it behaves similar to BufWriter.  Instead of buffering the `Write`
//! object passed to it, `LzmaWriter` applies compression or decompression to it as it's write.
//!
//!
//! # Examples
//!
//! ```no_run
//! use lzma::LzmaWriter;
//! use std::io::prelude::*;
//! use std::fs::File;
//!
//! let f = File::create("foo.xz").unwrap();
//! let mut f = LzmaWriter::new_compressor(f, 6).unwrap();
//!
//! write!(f, "It's a small world!").unwrap();
//! f.finish().unwrap();
//! ```

use std::io::{self, Write};
use std::ops::Drop;
use lzma_sys::*;
use std;
use error::LzmaError;
use ::Direction;
use lzma_stream_wrapper::LzmaStreamWrapper;

const DEFAULT_BUF_SIZE: usize = 4 * 1024;

pub struct LzmaWriter<T> {
	inner: T,
	stream: LzmaStreamWrapper,
	buffer: Vec<u8>,
	direction: Direction,
}

impl<T: Write> LzmaWriter<T> {
	pub fn new_compressor(inner: T, preset: u32) -> Result<LzmaWriter<T>, LzmaError> {
		LzmaWriter::with_capacity(DEFAULT_BUF_SIZE, inner, Direction::Compress, preset)
	}

	pub fn new_decompressor(inner: T) -> Result<LzmaWriter<T>, LzmaError> {
		LzmaWriter::with_capacity(DEFAULT_BUF_SIZE, inner, Direction::Decompress, 0)
	}

	pub fn with_capacity(capacity: usize, inner: T, direction: Direction, preset: u32) -> Result<LzmaWriter<T>, LzmaError> {
		let mut writer = LzmaWriter {
			inner: inner,
			stream: LzmaStreamWrapper::new(),
			buffer: vec![0; capacity],
			direction: direction,
		};

		match writer.direction {
			Direction::Compress => {
				try!(writer.stream.easy_encoder(preset, lzma_check::LZMA_CHECK_CRC64))
			},
			Direction::Decompress => {
				try!(writer.stream.stream_decoder(std::u64::MAX, 0))
			},
		}

		Ok(writer)
	}
}

impl<T> Drop for LzmaWriter<T> {
	fn drop(&mut self) {
		self.stream.end()
	}
}

impl<W: Write> LzmaWriter<W> {
	/// Finalizes the LZMA stream so that it finishes compressing or decompressing.
	///
	/// This *must* be called after all writing is done to ensure the last pieces of the compressed
	/// or decompressed stream get written out.
	pub fn finish(&mut self) -> Result<(), LzmaError> {
		loop {
			match self.lzma_code_and_write(&[], lzma_action::LZMA_FINISH) {
				Ok((lzma_ret::LZMA_STREAM_END,_)) => break,
				Ok(_) => (),
				Err(err) => return Err(err),
			}
		}

		Ok(())
	}

	fn lzma_code_and_write(&mut self, input: &[u8], action: lzma_action) -> Result<(lzma_ret, usize), LzmaError> {
		let result = self.stream.code(input, &mut self.buffer, action);
		let ret = try!(result.ret);

		if result.bytes_written > 0 {
			try!(Write::write_all(&mut self.inner, &self.buffer[..result.bytes_written]));
		}

		Ok((ret, result.bytes_read))
	}
}

impl<W: Write> Write for LzmaWriter<W> {
	fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
		match self.lzma_code_and_write(buf, lzma_action::LZMA_RUN) {
			Ok((_,bytes_read)) => Ok(bytes_read),
			Err(LzmaError::Io(err)) => return Err(err),
			Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err)),
		}
	}

	fn flush(&mut self) -> io::Result<()> {
		self.inner.flush()
	}
}
<commit_msg>Return the consumed inner Writer after finishing<commit_after>//! This module implements `LzmaWriter`.
//!
//! `LzmaWriter` implements the LZMA (XZ) compression/decompression algorithm as a generic
//! Writer.  In other words, it behaves similar to BufWriter.  Instead of buffering the `Write`
//! object passed to it, `LzmaWriter` applies compression or decompression to it as it's write.
//!
//!
//! # Examples
//!
//! ```no_run
//! use lzma::LzmaWriter;
//! use std::io::prelude::*;
//! use std::fs::File;
//!
//! let f = File::create("foo.xz").unwrap();
//! let mut f = LzmaWriter::new_compressor(f, 6).unwrap();
//!
//! write!(f, "It's a small world!").unwrap();
//! f.finish().unwrap();
//! ```

use std::io::{self, Write};
use lzma_sys::*;
use std;
use error::LzmaError;
use ::Direction;
use lzma_stream_wrapper::LzmaStreamWrapper;

const DEFAULT_BUF_SIZE: usize = 4 * 1024;

pub struct LzmaWriter<T> {
	inner: T,
	stream: LzmaStreamWrapper,
	buffer: Vec<u8>,
	direction: Direction,
}

impl<T: Write> LzmaWriter<T> {
	pub fn new_compressor(inner: T, preset: u32) -> Result<LzmaWriter<T>, LzmaError> {
		LzmaWriter::with_capacity(DEFAULT_BUF_SIZE, inner, Direction::Compress, preset)
	}

	pub fn new_decompressor(inner: T) -> Result<LzmaWriter<T>, LzmaError> {
		LzmaWriter::with_capacity(DEFAULT_BUF_SIZE, inner, Direction::Decompress, 0)
	}

	pub fn with_capacity(capacity: usize, inner: T, direction: Direction, preset: u32) -> Result<LzmaWriter<T>, LzmaError> {
		let mut writer = LzmaWriter {
			inner: inner,
			stream: LzmaStreamWrapper::new(),
			buffer: vec![0; capacity],
			direction: direction,
		};

		match writer.direction {
			Direction::Compress => {
				try!(writer.stream.easy_encoder(preset, lzma_check::LZMA_CHECK_CRC64))
			},
			Direction::Decompress => {
				try!(writer.stream.stream_decoder(std::u64::MAX, 0))
			},
		}

		Ok(writer)
	}
}

impl<W: Write> LzmaWriter<W> {
	/// Finalizes the LZMA stream so that it finishes compressing or decompressing.
	///
	/// This *must* be called after all writing is done to ensure the last pieces of the compressed
	/// or decompressed stream get written out.
	pub fn finish(mut self) -> Result<W, LzmaError> {
		loop {
			match self.lzma_code_and_write(&[], lzma_action::LZMA_FINISH) {
				Ok((lzma_ret::LZMA_STREAM_END,_)) => break,
				Ok(_) => (),
				Err(err) => return Err(err),
			}
		}

		self.stream.end();

		Ok(self.inner)
	}

	fn lzma_code_and_write(&mut self, input: &[u8], action: lzma_action) -> Result<(lzma_ret, usize), LzmaError> {
		let result = self.stream.code(input, &mut self.buffer, action);
		let ret = try!(result.ret);

		if result.bytes_written > 0 {
			try!(Write::write_all(&mut self.inner, &self.buffer[..result.bytes_written]));
		}

		Ok((ret, result.bytes_read))
	}
}

impl<W: Write> Write for LzmaWriter<W> {
	fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
		match self.lzma_code_and_write(buf, lzma_action::LZMA_RUN) {
			Ok((_,bytes_read)) => Ok(bytes_read),
			Err(LzmaError::Io(err)) => return Err(err),
			Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err)),
		}
	}

	fn flush(&mut self) -> io::Result<()> {
		self.inner.flush()
	}
}
<|endoftext|>


<commit_before>// Copyright 2012-2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

// ignore-test arcs no longer unwrap

extern crate sync;

use std::from_str::FromStr;
use std::iter::count;
use std::cmp::min;
use std::os;
use std::vec::from_elem;
use sync::Arc;
use sync::RWArc;

fn A(i: uint, j: uint) -> f64 {
    ((i + j) * (i + j + 1) / 2 + i + 1) as f64
}

fn dot(v: &[f64], u: &[f64]) -> f64 {
    let mut sum = 0.0;
    for (&v_i, &u_i) in v.iter().zip(u.iter()) {
        sum += v_i * u_i;
    }
    sum
}

fn mult(v: RWArc<~[f64]>, out: RWArc<~[f64]>, f: fn(&~[f64], uint) -> f64) {
    let wait = Arc::new(());
    let len = out.read(|out| out.len());
    let chunk = len / 100 + 1;
    for chk in count(0, chunk) {
        if chk >= len {break;}
        let w = wait.clone();
        let v = v.clone();
        let out = out.clone();
        spawn(proc() {
            for i in range(chk, min(len, chk + chunk)) {
                let val = v.read(|v| f(v, i));
                out.write(|out| out[i] = val);
            }
            let _ = w;
        });
    }
    let _ = wait.unwrap();
}

fn mult_Av_impl(v: &~[f64], i: uint) -> f64 {
    let mut sum = 0.;
    for (j, &v_j) in v.iter().enumerate() {
        sum += v_j / A(i, j);
    }
    sum
}

fn mult_Av(v: RWArc<~[f64]>, out: RWArc<~[f64]>) {
    mult(v, out, mult_Av_impl);
}

fn mult_Atv_impl(v: &~[f64], i: uint) -> f64 {
    let mut sum = 0.;
    for (j, &v_j) in v.iter().enumerate() {
        sum += v_j / A(j, i);
    }
    sum
}

fn mult_Atv(v: RWArc<~[f64]>, out: RWArc<~[f64]>) {
    mult(v, out, mult_Atv_impl);
}

fn mult_AtAv(v: RWArc<~[f64]>, out: RWArc<~[f64]>, tmp: RWArc<~[f64]>) {
    mult_Av(v, tmp.clone());
    mult_Atv(tmp, out);
}

fn main() {
    let args = os::args();
    let n = if os::getenv("RUST_BENCH").is_some() {
        5500
    } else if args.len() < 2 {
        2000
    } else {
        FromStr::from_str(args[1]).unwrap()
    };
    let u = RWArc::new(from_elem(n, 1.));
    let v = RWArc::new(from_elem(n, 1.));
    let tmp = RWArc::new(from_elem(n, 1.));
    for _ in range(0, 10) {
        mult_AtAv(u.clone(), v.clone(), tmp.clone());
        mult_AtAv(v.clone(), u.clone(), tmp.clone());
    }
    let u = u.unwrap();
    let v = v.unwrap();
    println!("{:.9f}", (dot(u,v) / dot(v,v)).sqrt());
}
<commit_msg>auto merge of #12766 : TeXitoi/rust/fix-shootout-spectralnorm, r=alexcrichton<commit_after>// Copyright 2012-2014 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.

extern crate sync;

use std::from_str::FromStr;
use std::iter::count;
use std::cmp::min;
use std::os;
use std::vec::from_elem;
use sync::RWArc;

fn A(i: uint, j: uint) -> f64 {
    ((i + j) * (i + j + 1) / 2 + i + 1) as f64
}

fn dot(v: &[f64], u: &[f64]) -> f64 {
    let mut sum = 0.0;
    for (&v_i, &u_i) in v.iter().zip(u.iter()) {
        sum += v_i * u_i;
    }
    sum
}

fn mult(v: RWArc<~[f64]>, out: RWArc<~[f64]>, f: fn(&~[f64], uint) -> f64) {
    // We lanch in different tasks the work to be done.  To finish
    // this fuction, we need to wait for the completion of every
    // tasks.  To do that, we give to each tasks a wait_chan that we
    // drop at the end of the work.  At the end of this function, we
    // wait until the channel hang up.
    let (wait_port, wait_chan) = Chan::new();

    let len = out.read(|out| out.len());
    let chunk = len / 100 + 1;
    for chk in count(0, chunk) {
        if chk >= len {break;}
        let w = wait_chan.clone();
        let v = v.clone();
        let out = out.clone();
        spawn(proc() {
            for i in range(chk, min(len, chk + chunk)) {
                let val = v.read(|v| f(v, i));
                out.write(|out| out[i] = val);
            }
            drop(w)
        });
    }

    // wait until the channel hang up (every task finished)
    drop(wait_chan);
    for () in wait_port.iter() {}
}

fn mult_Av_impl(v: &~[f64], i: uint) -> f64 {
    let mut sum = 0.;
    for (j, &v_j) in v.iter().enumerate() {
        sum += v_j / A(i, j);
    }
    sum
}

fn mult_Av(v: RWArc<~[f64]>, out: RWArc<~[f64]>) {
    mult(v, out, mult_Av_impl);
}

fn mult_Atv_impl(v: &~[f64], i: uint) -> f64 {
    let mut sum = 0.;
    for (j, &v_j) in v.iter().enumerate() {
        sum += v_j / A(j, i);
    }
    sum
}

fn mult_Atv(v: RWArc<~[f64]>, out: RWArc<~[f64]>) {
    mult(v, out, mult_Atv_impl);
}

fn mult_AtAv(v: RWArc<~[f64]>, out: RWArc<~[f64]>, tmp: RWArc<~[f64]>) {
    mult_Av(v, tmp.clone());
    mult_Atv(tmp, out);
}

fn main() {
    let args = os::args();
    let n = if os::getenv("RUST_BENCH").is_some() {
        5500
    } else if args.len() < 2 {
        2000
    } else {
        FromStr::from_str(args[1]).unwrap()
    };
    let u = RWArc::new(from_elem(n, 1.));
    let v = RWArc::new(from_elem(n, 1.));
    let tmp = RWArc::new(from_elem(n, 1.));
    for _ in range(0, 10) {
        mult_AtAv(u.clone(), v.clone(), tmp.clone());
        mult_AtAv(v.clone(), u.clone(), tmp.clone());
    }

    u.read(|u| v.read(|v| {
        println!("{:.9f}", (dot(*u, *v) / dot(*v, *v)).sqrt());
    }))
}
<|endoftext|>


<commit_before>// Copyright 2021 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Server stat collection types, to be used by tests, logging or GUI/TUI
//! to collect information about server status

use crate::util::RwLock;
use std::sync::Arc;
use std::time::SystemTime;

use crate::core::core::hash::Hash;
use crate::core::ser::ProtocolVersion;

use chrono::prelude::*;

use crate::chain::SyncStatus;
use crate::p2p;
use crate::p2p::Capabilities;
use grin_core::pow::Difficulty;

/// Server state info collection struct, to be passed around into internals
/// and populated when required
#[derive(Clone)]
pub struct ServerStateInfo {
	/// Stratum stats
	pub stratum_stats: Arc<RwLock<StratumStats>>,
}

impl Default for ServerStateInfo {
	fn default() -> ServerStateInfo {
		ServerStateInfo {
			stratum_stats: Arc::new(RwLock::new(StratumStats::default())),
		}
	}
}
/// Simpler thread-unaware version of above to be populated and returned to
/// consumers might be interested in, such as test results or UI
#[derive(Clone)]
pub struct ServerStats {
	/// Number of peers
	pub peer_count: u32,
	/// Chain head
	pub chain_stats: ChainStats,
	/// Header head (may differ from chain head)
	pub header_stats: ChainStats,
	/// Whether we're currently syncing
	pub sync_status: SyncStatus,
	/// Handle to current stratum server stats
	pub stratum_stats: StratumStats,
	/// Peer stats
	pub peer_stats: Vec<PeerStats>,
	/// Difficulty calculation statistics
	pub diff_stats: DiffStats,
	/// Transaction pool statistics
	pub tx_stats: Option<TxStats>,
	/// Disk usage in GB
	pub disk_usage_gb: String,
}

/// Chain Statistics
#[derive(Clone, Serialize, Debug)]
pub struct ChainStats {
	/// Height of the tip (max height of the fork)
	pub height: u64,
	/// Last block pushed to the fork
	pub last_block_h: Hash,
	/// Total difficulty accumulated on that fork
	pub total_difficulty: Difficulty,
	/// Timestamp of highest block or header
	pub latest_timestamp: DateTime<Utc>,
}
/// Transaction Statistics
#[derive(Clone, Serialize, Debug)]
pub struct TxStats {
	/// Number of transactions in the transaction pool
	pub tx_pool_size: usize,
	/// Number of transaction kernels in the transaction pool
	pub tx_pool_kernels: usize,
	/// Number of transactions in the stem pool
	pub stem_pool_size: usize,
	/// Number of transaction kernels in the stem pool
	pub stem_pool_kernels: usize,
}
/// Struct to return relevant information about stratum workers
#[derive(Clone, Serialize, Debug)]
pub struct WorkerStats {
	/// Unique ID for this worker
	pub id: String,
	/// whether stratum worker is currently connected
	pub is_connected: bool,
	/// Timestamp of most recent communication with this worker
	pub last_seen: SystemTime,
	/// which block height it starts mining
	pub initial_block_height: u64,
	/// pow difficulty this worker is using
	pub pow_difficulty: u64,
	/// number of valid shares submitted
	pub num_accepted: u64,
	/// number of invalid shares submitted
	pub num_rejected: u64,
	/// number of shares submitted too late
	pub num_stale: u64,
	/// number of valid blocks found
	pub num_blocks_found: u64,
}

/// Struct to return relevant information about the stratum server
#[derive(Clone, Serialize, Debug)]
pub struct StratumStats {
	/// whether stratum server is enabled
	pub is_enabled: bool,
	/// whether stratum server is running
	pub is_running: bool,
	/// Number of connected workers
	pub num_workers: usize,
	/// what block height we're mining at
	pub block_height: u64,
	/// current network difficulty we're working on
	pub network_difficulty: u64,
	/// cuckoo size of last share submitted
	pub edge_bits: u16,
	/// Number of blocks found by all workers
	pub blocks_found: u16,
	/// current network Hashrate (for edge_bits)
	pub network_hashrate: f64,
	/// The minimum acceptable share difficulty to request from miners
	pub minimum_share_difficulty: u64,
	/// Individual worker status
	pub worker_stats: Vec<WorkerStats>,
}

/// Stats on the last WINDOW blocks and the difficulty calculation
#[derive(Clone)]
pub struct DiffStats {
	/// latest height
	pub height: u64,
	/// Last WINDOW block data
	pub last_blocks: Vec<DiffBlock>,
	/// Average block time for last WINDOW blocks
	pub average_block_time: u64,
	/// Average WINDOW difficulty
	pub average_difficulty: u64,
	/// WINDOW size
	pub window_size: u64,
}

/// Last n blocks for difficulty calculation purposes
#[derive(Clone, Debug)]
pub struct DiffBlock {
	/// Block height (can be negative for a new chain)
	pub block_height: i64,
	/// Block hash (may be synthetic for a new chain)
	pub block_hash: Hash,
	/// Block network difficulty
	pub difficulty: u64,
	/// Time block was found (epoch seconds)
	pub time: u64,
	/// Duration since previous block (epoch seconds)
	pub duration: u64,
	/// secondary scaling
	pub secondary_scaling: u32,
	/// is secondary
	pub is_secondary: bool,
}

/// Struct to return relevant information about peers
#[derive(Clone, Debug)]
pub struct PeerStats {
	/// Current state of peer
	pub state: String,
	/// Address
	pub addr: String,
	/// version running
	pub version: ProtocolVersion,
	/// Peer user agent string.
	pub user_agent: String,
	/// difficulty reported by peer
	pub total_difficulty: u64,
	/// height reported by peer on ping
	pub height: u64,
	/// direction
	pub direction: String,
	/// Last time we saw a ping/pong from this peer.
	pub last_seen: DateTime<Utc>,
	/// Number of bytes we've sent to the peer.
	pub sent_bytes_per_sec: u64,
	/// Number of bytes we've received from the peer.
	pub received_bytes_per_sec: u64,
	/// Peer advertised capability flags.
	pub capabilities: Capabilities,
}

impl PartialEq for PeerStats {
	fn eq(&self, other: &PeerStats) -> bool {
		*self.addr == other.addr
	}
}

impl PartialEq for WorkerStats {
	fn eq(&self, other: &WorkerStats) -> bool {
		*self.id == other.id
	}
}

impl PartialEq for DiffBlock {
	fn eq(&self, other: &DiffBlock) -> bool {
		self.block_height == other.block_height
	}
}

impl PeerStats {
	/// Convert from a peer directly
	pub fn from_peer(peer: &p2p::Peer) -> PeerStats {
		// State
		let state = if peer.is_banned() {
			"Banned"
		} else if peer.is_connected() {
			"Connected"
		} else {
			"Disconnected"
		};
		let addr = peer.info.addr.to_string();
		let direction = match peer.info.direction {
			p2p::types::Direction::Inbound => "Inbound",
			p2p::types::Direction::Outbound => "Outbound",
		};
		PeerStats {
			state: state.to_string(),
			addr: addr,
			version: peer.info.version,
			user_agent: peer.info.user_agent.clone(),
			total_difficulty: peer.info.total_difficulty().to_num(),
			height: peer.info.height(),
			direction: direction.to_string(),
			last_seen: peer.info.last_seen(),
			sent_bytes_per_sec: peer.tracker().sent_bytes.read().bytes_per_min() / 60,
			received_bytes_per_sec: peer.tracker().received_bytes.read().bytes_per_min() / 60,
			capabilities: peer.info.capabilities,
		}
	}
}

impl Default for WorkerStats {
	fn default() -> WorkerStats {
		WorkerStats {
			id: String::from("unknown"),
			is_connected: false,
			last_seen: SystemTime::now(),
			initial_block_height: 0,
			pow_difficulty: 0,
			num_accepted: 0,
			num_rejected: 0,
			num_stale: 0,
			num_blocks_found: 0,
		}
	}
}

impl Default for StratumStats {
	fn default() -> StratumStats {
		StratumStats {
			is_enabled: false,
			is_running: false,
			num_workers: 0,
			block_height: 0,
			network_difficulty: 0,
			edge_bits: 32,
			blocks_found: 0,
			network_hashrate: 0.0,
			minimum_share_difficulty: 1,
			worker_stats: Vec::new(),
		}
	}
}
<commit_msg>derive debug for ServerStats (#3731)<commit_after>// Copyright 2021 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Server stat collection types, to be used by tests, logging or GUI/TUI
//! to collect information about server status

use crate::util::RwLock;
use std::sync::Arc;
use std::time::SystemTime;

use crate::core::core::hash::Hash;
use crate::core::ser::ProtocolVersion;

use chrono::prelude::*;

use crate::chain::SyncStatus;
use crate::p2p;
use crate::p2p::Capabilities;
use grin_core::pow::Difficulty;

/// Server state info collection struct, to be passed around into internals
/// and populated when required
#[derive(Clone)]
pub struct ServerStateInfo {
	/// Stratum stats
	pub stratum_stats: Arc<RwLock<StratumStats>>,
}

impl Default for ServerStateInfo {
	fn default() -> ServerStateInfo {
		ServerStateInfo {
			stratum_stats: Arc::new(RwLock::new(StratumStats::default())),
		}
	}
}
/// Simpler thread-unaware version of above to be populated and returned to
/// consumers might be interested in, such as test results or UI
#[derive(Debug, Clone)]
pub struct ServerStats {
	/// Number of peers
	pub peer_count: u32,
	/// Chain head
	pub chain_stats: ChainStats,
	/// Header head (may differ from chain head)
	pub header_stats: ChainStats,
	/// Whether we're currently syncing
	pub sync_status: SyncStatus,
	/// Handle to current stratum server stats
	pub stratum_stats: StratumStats,
	/// Peer stats
	pub peer_stats: Vec<PeerStats>,
	/// Difficulty calculation statistics
	pub diff_stats: DiffStats,
	/// Transaction pool statistics
	pub tx_stats: Option<TxStats>,
	/// Disk usage in GB
	pub disk_usage_gb: String,
}

/// Chain Statistics
#[derive(Clone, Serialize, Debug)]
pub struct ChainStats {
	/// Height of the tip (max height of the fork)
	pub height: u64,
	/// Last block pushed to the fork
	pub last_block_h: Hash,
	/// Total difficulty accumulated on that fork
	pub total_difficulty: Difficulty,
	/// Timestamp of highest block or header
	pub latest_timestamp: DateTime<Utc>,
}
/// Transaction Statistics
#[derive(Clone, Serialize, Debug)]
pub struct TxStats {
	/// Number of transactions in the transaction pool
	pub tx_pool_size: usize,
	/// Number of transaction kernels in the transaction pool
	pub tx_pool_kernels: usize,
	/// Number of transactions in the stem pool
	pub stem_pool_size: usize,
	/// Number of transaction kernels in the stem pool
	pub stem_pool_kernels: usize,
}
/// Struct to return relevant information about stratum workers
#[derive(Clone, Serialize, Debug)]
pub struct WorkerStats {
	/// Unique ID for this worker
	pub id: String,
	/// whether stratum worker is currently connected
	pub is_connected: bool,
	/// Timestamp of most recent communication with this worker
	pub last_seen: SystemTime,
	/// which block height it starts mining
	pub initial_block_height: u64,
	/// pow difficulty this worker is using
	pub pow_difficulty: u64,
	/// number of valid shares submitted
	pub num_accepted: u64,
	/// number of invalid shares submitted
	pub num_rejected: u64,
	/// number of shares submitted too late
	pub num_stale: u64,
	/// number of valid blocks found
	pub num_blocks_found: u64,
}

/// Struct to return relevant information about the stratum server
#[derive(Clone, Serialize, Debug)]
pub struct StratumStats {
	/// whether stratum server is enabled
	pub is_enabled: bool,
	/// whether stratum server is running
	pub is_running: bool,
	/// Number of connected workers
	pub num_workers: usize,
	/// what block height we're mining at
	pub block_height: u64,
	/// current network difficulty we're working on
	pub network_difficulty: u64,
	/// cuckoo size of last share submitted
	pub edge_bits: u16,
	/// Number of blocks found by all workers
	pub blocks_found: u16,
	/// current network Hashrate (for edge_bits)
	pub network_hashrate: f64,
	/// The minimum acceptable share difficulty to request from miners
	pub minimum_share_difficulty: u64,
	/// Individual worker status
	pub worker_stats: Vec<WorkerStats>,
}

/// Stats on the last WINDOW blocks and the difficulty calculation
#[derive(Debug, Clone)]
pub struct DiffStats {
	/// latest height
	pub height: u64,
	/// Last WINDOW block data
	pub last_blocks: Vec<DiffBlock>,
	/// Average block time for last WINDOW blocks
	pub average_block_time: u64,
	/// Average WINDOW difficulty
	pub average_difficulty: u64,
	/// WINDOW size
	pub window_size: u64,
}

/// Last n blocks for difficulty calculation purposes
#[derive(Clone, Debug)]
pub struct DiffBlock {
	/// Block height (can be negative for a new chain)
	pub block_height: i64,
	/// Block hash (may be synthetic for a new chain)
	pub block_hash: Hash,
	/// Block network difficulty
	pub difficulty: u64,
	/// Time block was found (epoch seconds)
	pub time: u64,
	/// Duration since previous block (epoch seconds)
	pub duration: u64,
	/// secondary scaling
	pub secondary_scaling: u32,
	/// is secondary
	pub is_secondary: bool,
}

/// Struct to return relevant information about peers
#[derive(Clone, Debug)]
pub struct PeerStats {
	/// Current state of peer
	pub state: String,
	/// Address
	pub addr: String,
	/// version running
	pub version: ProtocolVersion,
	/// Peer user agent string.
	pub user_agent: String,
	/// difficulty reported by peer
	pub total_difficulty: u64,
	/// height reported by peer on ping
	pub height: u64,
	/// direction
	pub direction: String,
	/// Last time we saw a ping/pong from this peer.
	pub last_seen: DateTime<Utc>,
	/// Number of bytes we've sent to the peer.
	pub sent_bytes_per_sec: u64,
	/// Number of bytes we've received from the peer.
	pub received_bytes_per_sec: u64,
	/// Peer advertised capability flags.
	pub capabilities: Capabilities,
}

impl PartialEq for PeerStats {
	fn eq(&self, other: &PeerStats) -> bool {
		*self.addr == other.addr
	}
}

impl PartialEq for WorkerStats {
	fn eq(&self, other: &WorkerStats) -> bool {
		*self.id == other.id
	}
}

impl PartialEq for DiffBlock {
	fn eq(&self, other: &DiffBlock) -> bool {
		self.block_height == other.block_height
	}
}

impl PeerStats {
	/// Convert from a peer directly
	pub fn from_peer(peer: &p2p::Peer) -> PeerStats {
		// State
		let state = if peer.is_banned() {
			"Banned"
		} else if peer.is_connected() {
			"Connected"
		} else {
			"Disconnected"
		};
		let addr = peer.info.addr.to_string();
		let direction = match peer.info.direction {
			p2p::types::Direction::Inbound => "Inbound",
			p2p::types::Direction::Outbound => "Outbound",
		};
		PeerStats {
			state: state.to_string(),
			addr: addr,
			version: peer.info.version,
			user_agent: peer.info.user_agent.clone(),
			total_difficulty: peer.info.total_difficulty().to_num(),
			height: peer.info.height(),
			direction: direction.to_string(),
			last_seen: peer.info.last_seen(),
			sent_bytes_per_sec: peer.tracker().sent_bytes.read().bytes_per_min() / 60,
			received_bytes_per_sec: peer.tracker().received_bytes.read().bytes_per_min() / 60,
			capabilities: peer.info.capabilities,
		}
	}
}

impl Default for WorkerStats {
	fn default() -> WorkerStats {
		WorkerStats {
			id: String::from("unknown"),
			is_connected: false,
			last_seen: SystemTime::now(),
			initial_block_height: 0,
			pow_difficulty: 0,
			num_accepted: 0,
			num_rejected: 0,
			num_stale: 0,
			num_blocks_found: 0,
		}
	}
}

impl Default for StratumStats {
	fn default() -> StratumStats {
		StratumStats {
			is_enabled: false,
			is_running: false,
			num_workers: 0,
			block_height: 0,
			network_difficulty: 0,
			edge_bits: 32,
			blocks_found: 0,
			network_hashrate: 0.0,
			minimum_share_difficulty: 1,
			worker_stats: Vec::new(),
		}
	}
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::cmp;

use rand;
use rand::Rng;
use itertools::Itertools;
use rgsl::randist::multinomial::multinomial_pdf;

use bio::stats::{Prob, LogProb};

use io::codebook::{Codebook, FeatureID};

use model::readout::{Expressions, Counts, Miscalls, Xi};

pub trait AbstractFeatureModel {
    /// FeatureID of this feature.
    fn feature_id(&self) -> FeatureID;

    /// Neighbors of this feature.
    fn neighbors(&self) -> &[FeatureID];

    /// Minimum hamming distance between any pair of features.
    fn min_dist(&self) -> u8;

    /// Number of events (calls and miscalls) except dropout.
    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32;

    /// Dropout probability.
    fn prob_dropout(&self) -> f64;

    /// Probability of exact miscall to given neighbor index.
    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64;

    /// Probability of mismatching miscall to given neighbor index.
    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64;

    /// Calculate maximum likelihood estimate of miscalls given expression and store in the given
    /// arrays.
    /// Returns the total absolute changes to estimated miscall counts.
    fn mle_miscalls(
        &self,
        expressions: &Expressions,
        miscalls_exact: &mut Miscalls,
        miscalls_mismatch: &mut Miscalls,
        rng: &mut rand::StdRng
    ) {
        let x = expressions[self.feature_id()];

        // We need to ensure that the sum of the estimated miscalls does not exceed x.
        // Hence, we iterate randomly over the neighbors such that we can simply stop once that
        // happens without causing a bias.
        let mut idx = (0..self.neighbors().len()).collect_vec();
        rng.shuffle(&mut idx);

        // by stochastic rounding, we ensure that on average \sum x*p_i = x
        // numeric rounding would not work if many of the probabilities are small enough to yield
        // expectations less than 1
        let mut stochastic_round = |v: f64| v.floor() as u32 + (rng.next_f64() <= v % 1.0) as u32;

        let mut rest = x;
        for i in idx {
            let n = self.neighbors()[i];
            let miscall_exact = self.prob_miscall_exact(i) * x as f64;
            miscalls_exact.set(
                self.feature_id(), n, stochastic_round(miscall_exact), &mut rest
            );

            if self.min_dist() == 4 {
                let miscall_mismatch = self.prob_miscall_mismatch(i) * x as f64;
                let miscall_mismatch = stochastic_round(miscall_mismatch);
                miscalls_mismatch.set(
                    self.feature_id(), n, miscall_mismatch, &mut rest
                );
            }
        }
    }

    /// Calculate maximum likelihood estimate of expression given miscalls.
    /// Returns the absolute amount of change to the expression.
    fn mle_expression(
        &self,
        expressions: &mut Expressions,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> i32 {
        let event_count = self.event_count_non_dropout(miscalls_exact, miscalls_mismatch);

        let x_ = cmp::max(
            (event_count as f64 / (1.0 - self.prob_dropout())).floor() as u32,
            // or at least as many as we have events coming from this feature
            // TODO does this allow x to decrease properly?
            event_count
        );

        let x = expressions.get_mut(self.feature_id()).unwrap();

        let change = *x as i32 - x_ as i32;

        *x = x_;

        change
    }
}

pub struct FeatureModel {
    pub(crate) feature_id: FeatureID,
    event_probs: Vec<f64>,
    neighbors: Vec<FeatureID>,
    min_dist: u8,
    pub(crate) counts: Counts,
    event_counts: RefCell<Vec<u32>>
}

impl FeatureModel {
    pub fn new(
        feature: FeatureID,
        counts: Counts,
        codebook: &Codebook,
        xi: &Xi
    ) -> Self {
        assert!(
            codebook.min_dist == 2 || codebook.min_dist == 4,
            "unsupported hamming distance: {}, supported: 2, 4",
            codebook.min_dist
        );

        let feature_record = codebook.record(feature);

        let xi_call = xi.prob(feature_record.codeword(), feature_record.codeword());

        let prob_call_exact = xi_call[0];
        let prob_call_mismatch = if codebook.min_dist == 4 {
            xi_call[1]
        } else {
            LogProb::ln_zero()
        };

        let neighbors_4 = codebook.neighbors(feature, 4);
        let neighbors_2 = if codebook.min_dist == 2 {
            codebook.neighbors(feature, 2)
        } else {
            vec![]
        };

        let mut neighbors = neighbors_4.clone();
        neighbors.extend(&neighbors_2);

        let xi_miscall_4 = codebook.neighbors(feature, 4).iter().map(|&n| {
            xi.prob(feature_record.codeword(), codebook.record(n).codeword())
        }).collect_vec();
        let xi_miscall_2 = if codebook.min_dist == 2 {
            codebook.neighbors(feature, 2).iter().map(|&n| {
                xi.prob(feature_record.codeword(), codebook.record(n).codeword())
            }).collect_vec()
        } else {
            vec![]
        };

        let prob_miscall_exact = xi_miscall_4.iter().map(|xi| {
            xi[0]
        }).chain(xi_miscall_2.iter().map(|xi| xi[0])).collect_vec();

        let prob_miscall_mismatch = if codebook.min_dist == 4 {
            xi_miscall_4.iter().map(|xi| xi[1]).collect_vec()
        } else {
            vec![]
        };

        let mut total = vec![prob_call_exact, prob_call_mismatch];
        total.extend(&prob_miscall_exact);
        total.extend(&prob_miscall_mismatch);

        let prob_total = LogProb::ln_sum_exp(&total);
        assert!(*prob_total <= 0, "prob_total > 0 ({})", prob_total);

        let mut event_probs = vec![
            *Prob::from(prob_call_exact),
            *Prob::from(prob_call_mismatch),
            *Prob::from(prob_total.ln_one_minus_exp())
        ];
        event_probs.extend(prob_miscall_exact.iter().map(|&p| *Prob::from(p)));
        event_probs.extend(prob_miscall_mismatch.iter().map(|&p| *Prob::from(p)));

        debug!("{:?}", event_probs);

        FeatureModel {
            feature_id: feature,
            event_probs: event_probs,
            neighbors: neighbors,
            min_dist: codebook.min_dist,
            counts: counts,
            event_counts: RefCell::new(Vec::new())
        }
    }

    /// Calculate likelihood of given expression.
    pub fn likelihood(
        &self,
        x: u32,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> LogProb {
        let count_exact_miscalled = miscalls_exact.total_to(self.feature_id);
        let count_mismatch_miscalled = miscalls_mismatch.total_to(self.feature_id);

        if self.counts.exact < count_exact_miscalled {
            panic!("bug: likelihood cannot be calculated if exact miscalls exceed counts: counts={}, miscalls={}", self.counts.exact, count_exact_miscalled);
        }
        if self.counts.mismatch < count_mismatch_miscalled {
            panic!("bug: likelihood cannot be calculated if mismatch miscalls exceed counts: counts={}, miscalls={}", self.counts.mismatch, count_mismatch_miscalled);
        }
        let total_count = self.counts.exact - count_exact_miscalled +
                          self.counts.mismatch - count_mismatch_miscalled;
        if total_count > x {
            panic!("bug: event count exceeds expression: x={}, event count={}", x, total_count);
        }

        let calls_exact = self.counts.exact - count_exact_miscalled;
        let calls_mismatch = self.counts.mismatch - count_mismatch_miscalled;

        // setup event counts
        {
            let mut event_counts = self.event_counts.borrow_mut();
            event_counts.clear();
            event_counts.push(
                // exact calls
                calls_exact
            );
            event_counts.push(
                // mismatch calls
                calls_mismatch
            );
            event_counts.push(
                // dropouts (fill in later)
                0
            );
            // miscalls
            event_counts.extend(self.neighbors.iter().map(
                |&n| miscalls_exact.get(self.feature_id, n)
            ));
            if self.min_dist == 4 {
                event_counts.extend(
                    self.neighbors.iter().map(
                        |&n| miscalls_mismatch.get(self.feature_id, n)
                    )
                );
            }

            // set dropouts
            let total_counts = event_counts.iter().sum();
            assert!(x >= total_counts, "total event counts exceed expression: x={} {:?}", x, event_counts);
            let dropouts = x - total_counts;
            event_counts[2] = dropouts;
        }

        LogProb::from(Prob(
            multinomial_pdf(&self.event_probs, &self.event_counts.borrow())
        ))
    }

    /// Minimum expression given fixed miscalls.
    pub fn min_expression(&self, miscalls_exact: &Miscalls, miscalls_mismatch: &Miscalls) -> u32 {
        self.counts.exact.saturating_sub(miscalls_exact.total_to(self.feature_id)) +
        self.counts.mismatch.saturating_sub(miscalls_mismatch.total_to(self.feature_id)) +
        miscalls_exact.total_from(self.feature_id) + miscalls_mismatch.total_from(self.feature_id)
    }
}

impl AbstractFeatureModel for FeatureModel {
    fn feature_id(&self) -> FeatureID {
        self.feature_id
    }

    fn neighbors(&self) -> &[FeatureID] {
        &self.neighbors
    }

    fn min_dist(&self) -> u8 {
        self.min_dist
    }

    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32 {
        let calls_exact = self.counts.exact.saturating_sub(
            miscalls_exact.total_to(self.feature_id)
        );
        let calls_mismatch = self.counts.mismatch.saturating_sub(
            miscalls_mismatch.total_to(self.feature_id)
        );

        calls_exact + calls_mismatch +
        miscalls_exact.total_from(self.feature_id) +
        miscalls_mismatch.total_from(self.feature_id)
    }

    fn prob_dropout(&self) -> f64 {
        self.event_probs[2]
    }

    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64 {
        self.event_probs[3 + neighbor_index]
    }

    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64 {
        self.event_probs[3 + self.neighbors.len() + neighbor_index]
    }
}

pub struct NoiseModel {
    pub(crate) feature_id: FeatureID,
    pub(crate) not_expressed_feature_ids: Vec<FeatureID>,
    pub(crate) not_expressed_counts: Vec<Counts>,
    probs_miscall_exact: Vec<Prob>,
    probs_miscall_mismatch: Vec<Prob>,
    prob_dropout: Prob,
    neighbors: Vec<FeatureID>,
    min_dist: u8,
}

impl NoiseModel {
    pub fn new(
        feature_id: FeatureID,
        not_expressed_feature_ids: Vec<FeatureID>,
        not_expressed_counts: Vec<Counts>,
        codebook: &Codebook,
        xi: &Xi
    ) -> Self {
        assert!(
            codebook.min_dist == 2 || codebook.min_dist == 4,
            "unsupported hamming distance: {}, supported: 2, 4",
            codebook.min_dist
        );

        // miscalls generated from noise
        let xi_miscall = codebook.records().iter().filter_map(|feat_rec| {
            if feat_rec.expressed() {
                Some(xi.prob(codebook.noise().codeword(), feat_rec.codeword()))
            } else { None }
        }).collect_vec();
        let neighbors = codebook.records().iter().filter_map(|rec| {
            if rec.expressed() {
                Some(codebook.get_id(rec.name()))
            } else { None }
        }).collect_vec();

        let probs_miscall_exact = xi_miscall.iter().map(|xi| xi[0]).collect_vec();
        let probs_miscall_mismatch = if codebook.min_dist == 4 {
            xi_miscall.iter().map(|xi| xi[1]).collect_vec()
        } else {
            vec![]
        };

        let prob_total_miscall_exact = LogProb::ln_sum_exp(&probs_miscall_exact);
        let prob_total_miscall_mismatch = LogProb::ln_sum_exp(&probs_miscall_mismatch);

        // probs for calls of not expressed features
        let xi_not_expressed = not_expressed_feature_ids.iter().map(|&f| {
            xi.prob(codebook.noise().codeword(), codebook.record(f).codeword())
        }).collect_vec();

        let prob_not_expressed_exact = LogProb::ln_sum_exp(
            &xi_not_expressed.iter().map(|xi| xi[0]).collect_vec()
        );
        let prob_not_expressed_mismatch = LogProb::ln_sum_exp(
            &xi_not_expressed.iter().map(|xi| xi[1]).collect_vec()
        );

        // dropout
        let prob_dropout = LogProb::ln_sum_exp(&[
            prob_not_expressed_exact,
            prob_not_expressed_mismatch,
            prob_total_miscall_exact,
            prob_total_miscall_mismatch
        ]).ln_one_minus_exp();

        NoiseModel {
            feature_id: feature_id,
            not_expressed_feature_ids: not_expressed_feature_ids,
            not_expressed_counts: not_expressed_counts,
            probs_miscall_exact: probs_miscall_exact.iter().map(|&p| Prob::from(p)).collect_vec(),
            probs_miscall_mismatch: probs_miscall_mismatch.iter().map(|&p| Prob::from(p)).collect_vec(),
            prob_dropout: Prob::from(prob_dropout),
            neighbors: neighbors,
            min_dist: codebook.min_dist
        }
    }
}

impl AbstractFeatureModel for NoiseModel {
    fn feature_id(&self) -> FeatureID {
        self.feature_id
    }

    fn neighbors(&self) -> &[FeatureID] {
        &self.neighbors
    }

    fn min_dist(&self) -> u8 {
        self.min_dist
    }

    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32 {
        let mut calls_exact = 0;
        let mut calls_mismatch = 0;
        for (&id, counts) in self.not_expressed_feature_ids.iter().zip(&self.not_expressed_counts) {
            calls_exact += counts.exact.saturating_sub(miscalls_exact.total_to(id));
            calls_mismatch += counts.mismatch.saturating_sub(miscalls_mismatch.total_to(id));
        }

        let event_count = calls_exact + calls_mismatch +
                          miscalls_exact.total_from(self.feature_id) +
                          miscalls_mismatch.total_from(self.feature_id);

        event_count
    }

    fn prob_dropout(&self) -> f64 {
        *self.prob_dropout
    }

    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64 {
        *self.probs_miscall_exact[neighbor_index]
    }

    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64 {
        *self.probs_miscall_mismatch[neighbor_index]
    }
}
<commit_msg>Minor.<commit_after>use std::cell::RefCell;
use std::cmp;

use rand;
use rand::Rng;
use itertools::Itertools;
use rgsl::randist::multinomial::multinomial_pdf;

use bio::stats::{Prob, LogProb};

use io::codebook::{Codebook, FeatureID};

use model::readout::{Expressions, Counts, Miscalls, Xi};

pub trait AbstractFeatureModel {
    /// FeatureID of this feature.
    fn feature_id(&self) -> FeatureID;

    /// Neighbors of this feature.
    fn neighbors(&self) -> &[FeatureID];

    /// Minimum hamming distance between any pair of features.
    fn min_dist(&self) -> u8;

    /// Number of events (calls and miscalls) except dropout.
    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32;

    /// Dropout probability.
    fn prob_dropout(&self) -> f64;

    /// Probability of exact miscall to given neighbor index.
    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64;

    /// Probability of mismatching miscall to given neighbor index.
    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64;

    /// Calculate maximum likelihood estimate of miscalls given expression and store in the given
    /// arrays.
    /// Returns the total absolute changes to estimated miscall counts.
    fn mle_miscalls(
        &self,
        expressions: &Expressions,
        miscalls_exact: &mut Miscalls,
        miscalls_mismatch: &mut Miscalls,
        rng: &mut rand::StdRng
    ) {
        let x = expressions[self.feature_id()];

        // We need to ensure that the sum of the estimated miscalls does not exceed x.
        // Hence, we iterate randomly over the neighbors such that we can simply stop once that
        // happens without causing a bias.
        let mut idx = (0..self.neighbors().len()).collect_vec();
        rng.shuffle(&mut idx);

        // by stochastic rounding, we ensure that on average \sum x*p_i = x
        // numeric rounding would not work if many of the probabilities are small enough to yield
        // expectations less than 1
        let mut stochastic_round = |v: f64| v.floor() as u32 + (rng.next_f64() <= v % 1.0) as u32;

        let mut rest = x;
        for i in idx {
            let n = self.neighbors()[i];
            let miscall_exact = self.prob_miscall_exact(i) * x as f64;
            miscalls_exact.set(
                self.feature_id(), n, stochastic_round(miscall_exact), &mut rest
            );

            if self.min_dist() == 4 {
                let miscall_mismatch = self.prob_miscall_mismatch(i) * x as f64;
                let miscall_mismatch = stochastic_round(miscall_mismatch);
                miscalls_mismatch.set(
                    self.feature_id(), n, miscall_mismatch, &mut rest
                );
            }
        }
    }

    /// Calculate maximum likelihood estimate of expression given miscalls.
    /// Returns the absolute amount of change to the expression.
    fn mle_expression(
        &self,
        expressions: &mut Expressions,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> i32 {
        let event_count = self.event_count_non_dropout(miscalls_exact, miscalls_mismatch);

        let x_ = cmp::max(
            (event_count as f64 / (1.0 - self.prob_dropout())).floor() as u32,
            // or at least as many as we have events coming from this feature
            // TODO does this allow x to decrease properly?
            event_count
        );

        let x = expressions.get_mut(self.feature_id()).unwrap();

        let change = *x as i32 - x_ as i32;

        *x = x_;

        change
    }
}

pub struct FeatureModel {
    pub(crate) feature_id: FeatureID,
    event_probs: Vec<f64>,
    neighbors: Vec<FeatureID>,
    min_dist: u8,
    pub(crate) counts: Counts,
    event_counts: RefCell<Vec<u32>>
}

impl FeatureModel {
    pub fn new(
        feature: FeatureID,
        counts: Counts,
        codebook: &Codebook,
        xi: &Xi
    ) -> Self {
        assert!(
            codebook.min_dist == 2 || codebook.min_dist == 4,
            "unsupported hamming distance: {}, supported: 2, 4",
            codebook.min_dist
        );

        let feature_record = codebook.record(feature);

        let xi_call = xi.prob(feature_record.codeword(), feature_record.codeword());

        let prob_call_exact = xi_call[0];
        let prob_call_mismatch = if codebook.min_dist == 4 {
            xi_call[1]
        } else {
            LogProb::ln_zero()
        };

        let neighbors_4 = codebook.neighbors(feature, 4);
        let neighbors_2 = if codebook.min_dist == 2 {
            codebook.neighbors(feature, 2)
        } else {
            vec![]
        };

        let mut neighbors = neighbors_4.clone();
        neighbors.extend(&neighbors_2);

        let xi_miscall_4 = codebook.neighbors(feature, 4).iter().map(|&n| {
            xi.prob(feature_record.codeword(), codebook.record(n).codeword())
        }).collect_vec();
        let xi_miscall_2 = if codebook.min_dist == 2 {
            codebook.neighbors(feature, 2).iter().map(|&n| {
                xi.prob(feature_record.codeword(), codebook.record(n).codeword())
            }).collect_vec()
        } else {
            vec![]
        };

        let prob_miscall_exact = xi_miscall_4.iter().map(|xi| {
            xi[0]
        }).chain(xi_miscall_2.iter().map(|xi| xi[0])).collect_vec();

        let prob_miscall_mismatch = if codebook.min_dist == 4 {
            xi_miscall_4.iter().map(|xi| xi[1]).collect_vec()
        } else {
            vec![]
        };

        let mut total = vec![prob_call_exact, prob_call_mismatch];
        total.extend(&prob_miscall_exact);
        total.extend(&prob_miscall_mismatch);

        let prob_total = LogProb::ln_sum_exp(&total);
        assert!(*prob_total <= 0.0, "prob_total > 0 ({})", prob_total);

        let mut event_probs = vec![
            *Prob::from(prob_call_exact),
            *Prob::from(prob_call_mismatch),
            *Prob::from(prob_total.ln_one_minus_exp())
        ];
        event_probs.extend(prob_miscall_exact.iter().map(|&p| *Prob::from(p)));
        event_probs.extend(prob_miscall_mismatch.iter().map(|&p| *Prob::from(p)));

        debug!("{:?}", event_probs);

        FeatureModel {
            feature_id: feature,
            event_probs: event_probs,
            neighbors: neighbors,
            min_dist: codebook.min_dist,
            counts: counts,
            event_counts: RefCell::new(Vec::new())
        }
    }

    /// Calculate likelihood of given expression.
    pub fn likelihood(
        &self,
        x: u32,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> LogProb {
        let count_exact_miscalled = miscalls_exact.total_to(self.feature_id);
        let count_mismatch_miscalled = miscalls_mismatch.total_to(self.feature_id);

        if self.counts.exact < count_exact_miscalled {
            panic!("bug: likelihood cannot be calculated if exact miscalls exceed counts: counts={}, miscalls={}", self.counts.exact, count_exact_miscalled);
        }
        if self.counts.mismatch < count_mismatch_miscalled {
            panic!("bug: likelihood cannot be calculated if mismatch miscalls exceed counts: counts={}, miscalls={}", self.counts.mismatch, count_mismatch_miscalled);
        }
        let total_count = self.counts.exact - count_exact_miscalled +
                          self.counts.mismatch - count_mismatch_miscalled;
        if total_count > x {
            panic!("bug: event count exceeds expression: x={}, event count={}", x, total_count);
        }

        let calls_exact = self.counts.exact - count_exact_miscalled;
        let calls_mismatch = self.counts.mismatch - count_mismatch_miscalled;

        // setup event counts
        {
            let mut event_counts = self.event_counts.borrow_mut();
            event_counts.clear();
            event_counts.push(
                // exact calls
                calls_exact
            );
            event_counts.push(
                // mismatch calls
                calls_mismatch
            );
            event_counts.push(
                // dropouts (fill in later)
                0
            );
            // miscalls
            event_counts.extend(self.neighbors.iter().map(
                |&n| miscalls_exact.get(self.feature_id, n)
            ));
            if self.min_dist == 4 {
                event_counts.extend(
                    self.neighbors.iter().map(
                        |&n| miscalls_mismatch.get(self.feature_id, n)
                    )
                );
            }

            // set dropouts
            let total_counts = event_counts.iter().sum();
            assert!(x >= total_counts, "total event counts exceed expression: x={} {:?}", x, event_counts);
            let dropouts = x - total_counts;
            event_counts[2] = dropouts;
        }

        LogProb::from(Prob(
            multinomial_pdf(&self.event_probs, &self.event_counts.borrow())
        ))
    }

    /// Minimum expression given fixed miscalls.
    pub fn min_expression(&self, miscalls_exact: &Miscalls, miscalls_mismatch: &Miscalls) -> u32 {
        self.counts.exact.saturating_sub(miscalls_exact.total_to(self.feature_id)) +
        self.counts.mismatch.saturating_sub(miscalls_mismatch.total_to(self.feature_id)) +
        miscalls_exact.total_from(self.feature_id) + miscalls_mismatch.total_from(self.feature_id)
    }
}

impl AbstractFeatureModel for FeatureModel {
    fn feature_id(&self) -> FeatureID {
        self.feature_id
    }

    fn neighbors(&self) -> &[FeatureID] {
        &self.neighbors
    }

    fn min_dist(&self) -> u8 {
        self.min_dist
    }

    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32 {
        let calls_exact = self.counts.exact.saturating_sub(
            miscalls_exact.total_to(self.feature_id)
        );
        let calls_mismatch = self.counts.mismatch.saturating_sub(
            miscalls_mismatch.total_to(self.feature_id)
        );

        calls_exact + calls_mismatch +
        miscalls_exact.total_from(self.feature_id) +
        miscalls_mismatch.total_from(self.feature_id)
    }

    fn prob_dropout(&self) -> f64 {
        self.event_probs[2]
    }

    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64 {
        self.event_probs[3 + neighbor_index]
    }

    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64 {
        self.event_probs[3 + self.neighbors.len() + neighbor_index]
    }
}

pub struct NoiseModel {
    pub(crate) feature_id: FeatureID,
    pub(crate) not_expressed_feature_ids: Vec<FeatureID>,
    pub(crate) not_expressed_counts: Vec<Counts>,
    probs_miscall_exact: Vec<Prob>,
    probs_miscall_mismatch: Vec<Prob>,
    prob_dropout: Prob,
    neighbors: Vec<FeatureID>,
    min_dist: u8,
}

impl NoiseModel {
    pub fn new(
        feature_id: FeatureID,
        not_expressed_feature_ids: Vec<FeatureID>,
        not_expressed_counts: Vec<Counts>,
        codebook: &Codebook,
        xi: &Xi
    ) -> Self {
        assert!(
            codebook.min_dist == 2 || codebook.min_dist == 4,
            "unsupported hamming distance: {}, supported: 2, 4",
            codebook.min_dist
        );

        // miscalls generated from noise
        let xi_miscall = codebook.records().iter().filter_map(|feat_rec| {
            if feat_rec.expressed() {
                Some(xi.prob(codebook.noise().codeword(), feat_rec.codeword()))
            } else { None }
        }).collect_vec();
        let neighbors = codebook.records().iter().filter_map(|rec| {
            if rec.expressed() {
                Some(codebook.get_id(rec.name()))
            } else { None }
        }).collect_vec();

        let probs_miscall_exact = xi_miscall.iter().map(|xi| xi[0]).collect_vec();
        let probs_miscall_mismatch = if codebook.min_dist == 4 {
            xi_miscall.iter().map(|xi| xi[1]).collect_vec()
        } else {
            vec![]
        };

        let prob_total_miscall_exact = LogProb::ln_sum_exp(&probs_miscall_exact);
        let prob_total_miscall_mismatch = LogProb::ln_sum_exp(&probs_miscall_mismatch);

        // probs for calls of not expressed features
        let xi_not_expressed = not_expressed_feature_ids.iter().map(|&f| {
            xi.prob(codebook.noise().codeword(), codebook.record(f).codeword())
        }).collect_vec();

        let prob_not_expressed_exact = LogProb::ln_sum_exp(
            &xi_not_expressed.iter().map(|xi| xi[0]).collect_vec()
        );
        let prob_not_expressed_mismatch = LogProb::ln_sum_exp(
            &xi_not_expressed.iter().map(|xi| xi[1]).collect_vec()
        );

        // dropout
        let prob_dropout = LogProb::ln_sum_exp(&[
            prob_not_expressed_exact,
            prob_not_expressed_mismatch,
            prob_total_miscall_exact,
            prob_total_miscall_mismatch
        ]).ln_one_minus_exp();

        NoiseModel {
            feature_id: feature_id,
            not_expressed_feature_ids: not_expressed_feature_ids,
            not_expressed_counts: not_expressed_counts,
            probs_miscall_exact: probs_miscall_exact.iter().map(|&p| Prob::from(p)).collect_vec(),
            probs_miscall_mismatch: probs_miscall_mismatch.iter().map(|&p| Prob::from(p)).collect_vec(),
            prob_dropout: Prob::from(prob_dropout),
            neighbors: neighbors,
            min_dist: codebook.min_dist
        }
    }
}

impl AbstractFeatureModel for NoiseModel {
    fn feature_id(&self) -> FeatureID {
        self.feature_id
    }

    fn neighbors(&self) -> &[FeatureID] {
        &self.neighbors
    }

    fn min_dist(&self) -> u8 {
        self.min_dist
    }

    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32 {
        let mut calls_exact = 0;
        let mut calls_mismatch = 0;
        for (&id, counts) in self.not_expressed_feature_ids.iter().zip(&self.not_expressed_counts) {
            calls_exact += counts.exact.saturating_sub(miscalls_exact.total_to(id));
            calls_mismatch += counts.mismatch.saturating_sub(miscalls_mismatch.total_to(id));
        }

        let event_count = calls_exact + calls_mismatch +
                          miscalls_exact.total_from(self.feature_id) +
                          miscalls_mismatch.total_from(self.feature_id);

        event_count
    }

    fn prob_dropout(&self) -> f64 {
        *self.prob_dropout
    }

    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64 {
        *self.probs_miscall_exact[neighbor_index]
    }

    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64 {
        *self.probs_miscall_mismatch[neighbor_index]
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use kite::{Document, Term, TermRef};
use kite::schema::FieldRef;
use kite::segment::Segment;
use byteorder::{LittleEndian, WriteBytesExt};
use roaring::RoaringBitmap;

use key_builder::KeyBuilder;

#[derive(Debug)]
pub struct SegmentBuilder {
    current_doc: u16,
    pub term_dictionary: HashMap<Term, TermRef>,
    current_term_ref: u32,
    pub term_directories: HashMap<(FieldRef, TermRef), RoaringBitmap>,
    pub statistics: HashMap<Vec<u8>, i64>,
    pub stored_field_values: HashMap<(FieldRef, u16, Vec<u8>), Vec<u8>>,
}

#[derive(Debug)]
pub enum DocumentInsertError {
    /// Segment couldn't hold any more docs
    SegmentFull,
}

impl SegmentBuilder {
    pub fn new() -> SegmentBuilder {
        SegmentBuilder {
            current_doc: 0,
            term_dictionary: HashMap::new(),
            current_term_ref: 0,
            term_directories: HashMap::new(),
            statistics: HashMap::new(),
            stored_field_values: HashMap::new(),
        }
    }

    fn get_term_ref(&mut self, term: &Term) -> TermRef {
        if let Some(term_ref) = self.term_dictionary.get(term) {
            return *term_ref;
        }

        // Add the term to the dictionary
        let term_ref = TermRef::new(self.current_term_ref);
        self.current_term_ref += 1;
        self.term_dictionary.insert(term.clone(), term_ref);

        term_ref
    }

    // TODO: Need to translate field names to field refs and terms to term refs
    pub fn add_document(&mut self, doc: &Document) -> Result<u16, DocumentInsertError> {
        // Get document ord
        let doc_id = self.current_doc;
        self.current_doc += 1;
        try!(self.current_doc.checked_add(1).ok_or(DocumentInsertError::SegmentFull));

        // Insert indexed fields
        let mut term_frequencies = HashMap::new();
        for (field, tokens) in doc.indexed_fields.iter() {
            let mut field_token_count = 0;

            for (term, positions) in tokens.iter() {
                let frequency = positions.len();
                field_token_count += frequency;

                // Get term ref
                let term_ref = self.get_term_ref(term);

                // Term frequency
                let mut term_frequency = term_frequencies.entry(term_ref).or_insert(0);
                *term_frequency += frequency;

                // Write directory list
                self.term_directories.entry((*field, term_ref)).or_insert_with(RoaringBitmap::new).insert(doc_id as u32);

                // Write term frequency
                // 1 is by far the most common frequency. At search time, we interpret a missing
                // key as meaning there is a term frequency of 1
                if frequency != 1 {
                    let mut value_type = vec![b't', b'f'];
                    value_type.extend(term_ref.ord().to_string().as_bytes());

                    let mut frequency_bytes: Vec<u8> = Vec::new();
                    frequency_bytes.write_i64::<LittleEndian>(frequency as i64).unwrap();

                    self.stored_field_values.insert((*field, doc_id, value_type), frequency_bytes);
                }

                // Increment term document frequency
                let stat_name = KeyBuilder::segment_stat_term_doc_frequency_stat_name(field.ord(), term_ref.ord());
                let mut stat = self.statistics.entry(stat_name).or_insert(0);
                *stat += 1;
            }

            // Field length
            // Used by the BM25 similarity model
            let length = ((field_token_count as f64).sqrt() - 1.0) * 3.0;
            let length = if length > 255.0 { 255.0 } else { length } as u8;
            if length != 0 {
                self.stored_field_values.insert((*field, doc_id, b"len".to_vec()), vec![length]);
            }

            // Increment total field docs
            {
                let stat_name = KeyBuilder::segment_stat_total_field_docs_stat_name(field.ord());
                let mut stat = self.statistics.entry(stat_name).or_insert(0);
                *stat += 1;
            }

            // Increment total field tokens
            {
                let stat_name = KeyBuilder::segment_stat_total_field_tokens_stat_name(field.ord());
                let mut stat = self.statistics.entry(stat_name).or_insert(0);
                *stat += field_token_count as i64;
            }
        }

        // Insert stored fields
        for (field, value) in doc.stored_fields.iter() {
            self.stored_field_values.insert((*field, doc_id, b"val".to_vec()), value.to_bytes());
        }

        // Increment total docs
        {
            let mut stat = self.statistics.entry(b"total_docs".to_vec()).or_insert(0);
            *stat += 1;
        }

        Ok(doc_id)
    }
}

impl Segment for SegmentBuilder {
    fn id(&self) -> u32 {
        0
    }

    fn load_statistic(&self, stat_name: &[u8]) -> Result<Option<i64>, String> {
        Ok(self.statistics.get(stat_name).cloned())
    }

    fn load_stored_field_value_raw(&self, doc_ord: u16, field_ref: FieldRef, value_type: &[u8]) -> Result<Option<Vec<u8>>, String> {
        Ok(self.stored_field_values.get(&(field_ref, doc_ord, value_type.to_vec())).cloned())
    }

    fn load_term_directory(&self, field_ref: FieldRef, term_ref: TermRef) -> Result<Option<RoaringBitmap>, String> {
        Ok(self.term_directories.get(&(field_ref, term_ref)).cloned())
    }

    fn load_deletion_list(&self) -> Result<Option<RoaringBitmap>, String> {
        Ok(None)
    }
}
<commit_msg>Removed unneeded TODO<commit_after>use std::collections::HashMap;

use kite::{Document, Term, TermRef};
use kite::schema::FieldRef;
use kite::segment::Segment;
use byteorder::{LittleEndian, WriteBytesExt};
use roaring::RoaringBitmap;

use key_builder::KeyBuilder;

#[derive(Debug)]
pub struct SegmentBuilder {
    current_doc: u16,
    pub term_dictionary: HashMap<Term, TermRef>,
    current_term_ref: u32,
    pub term_directories: HashMap<(FieldRef, TermRef), RoaringBitmap>,
    pub statistics: HashMap<Vec<u8>, i64>,
    pub stored_field_values: HashMap<(FieldRef, u16, Vec<u8>), Vec<u8>>,
}

#[derive(Debug)]
pub enum DocumentInsertError {
    /// Segment couldn't hold any more docs
    SegmentFull,
}

impl SegmentBuilder {
    pub fn new() -> SegmentBuilder {
        SegmentBuilder {
            current_doc: 0,
            term_dictionary: HashMap::new(),
            current_term_ref: 0,
            term_directories: HashMap::new(),
            statistics: HashMap::new(),
            stored_field_values: HashMap::new(),
        }
    }

    fn get_term_ref(&mut self, term: &Term) -> TermRef {
        if let Some(term_ref) = self.term_dictionary.get(term) {
            return *term_ref;
        }

        // Add the term to the dictionary
        let term_ref = TermRef::new(self.current_term_ref);
        self.current_term_ref += 1;
        self.term_dictionary.insert(term.clone(), term_ref);

        term_ref
    }

    pub fn add_document(&mut self, doc: &Document) -> Result<u16, DocumentInsertError> {
        // Get document ord
        let doc_id = self.current_doc;
        self.current_doc += 1;
        try!(self.current_doc.checked_add(1).ok_or(DocumentInsertError::SegmentFull));

        // Insert indexed fields
        let mut term_frequencies = HashMap::new();
        for (field, tokens) in doc.indexed_fields.iter() {
            let mut field_token_count = 0;

            for (term, positions) in tokens.iter() {
                let frequency = positions.len();
                field_token_count += frequency;

                // Get term ref
                let term_ref = self.get_term_ref(term);

                // Term frequency
                let mut term_frequency = term_frequencies.entry(term_ref).or_insert(0);
                *term_frequency += frequency;

                // Write directory list
                self.term_directories.entry((*field, term_ref)).or_insert_with(RoaringBitmap::new).insert(doc_id as u32);

                // Write term frequency
                // 1 is by far the most common frequency. At search time, we interpret a missing
                // key as meaning there is a term frequency of 1
                if frequency != 1 {
                    let mut value_type = vec![b't', b'f'];
                    value_type.extend(term_ref.ord().to_string().as_bytes());

                    let mut frequency_bytes: Vec<u8> = Vec::new();
                    frequency_bytes.write_i64::<LittleEndian>(frequency as i64).unwrap();

                    self.stored_field_values.insert((*field, doc_id, value_type), frequency_bytes);
                }

                // Increment term document frequency
                let stat_name = KeyBuilder::segment_stat_term_doc_frequency_stat_name(field.ord(), term_ref.ord());
                let mut stat = self.statistics.entry(stat_name).or_insert(0);
                *stat += 1;
            }

            // Field length
            // Used by the BM25 similarity model
            let length = ((field_token_count as f64).sqrt() - 1.0) * 3.0;
            let length = if length > 255.0 { 255.0 } else { length } as u8;
            if length != 0 {
                self.stored_field_values.insert((*field, doc_id, b"len".to_vec()), vec![length]);
            }

            // Increment total field docs
            {
                let stat_name = KeyBuilder::segment_stat_total_field_docs_stat_name(field.ord());
                let mut stat = self.statistics.entry(stat_name).or_insert(0);
                *stat += 1;
            }

            // Increment total field tokens
            {
                let stat_name = KeyBuilder::segment_stat_total_field_tokens_stat_name(field.ord());
                let mut stat = self.statistics.entry(stat_name).or_insert(0);
                *stat += field_token_count as i64;
            }
        }

        // Insert stored fields
        for (field, value) in doc.stored_fields.iter() {
            self.stored_field_values.insert((*field, doc_id, b"val".to_vec()), value.to_bytes());
        }

        // Increment total docs
        {
            let mut stat = self.statistics.entry(b"total_docs".to_vec()).or_insert(0);
            *stat += 1;
        }

        Ok(doc_id)
    }
}

impl Segment for SegmentBuilder {
    fn id(&self) -> u32 {
        0
    }

    fn load_statistic(&self, stat_name: &[u8]) -> Result<Option<i64>, String> {
        Ok(self.statistics.get(stat_name).cloned())
    }

    fn load_stored_field_value_raw(&self, doc_ord: u16, field_ref: FieldRef, value_type: &[u8]) -> Result<Option<Vec<u8>>, String> {
        Ok(self.stored_field_values.get(&(field_ref, doc_ord, value_type.to_vec())).cloned())
    }

    fn load_term_directory(&self, field_ref: FieldRef, term_ref: TermRef) -> Result<Option<RoaringBitmap>, String> {
        Ok(self.term_directories.get(&(field_ref, term_ref)).cloned())
    }

    fn load_deletion_list(&self) -> Result<Option<RoaringBitmap>, String> {
        Ok(None)
    }
}
<|endoftext|>


<commit_before>use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::path::Path;
use std::ptr;

use openexr_sys::*;

use error::*;
use frame_buffer::FrameBuffer;
use Header;

pub struct ScanlineOutputFile {
    handle: *mut CEXR_OutputFile,
    header_ref: Header,
    _phantom: PhantomData<CEXR_OutputFile>,
}

impl ScanlineOutputFile {
    pub fn new(path: &Path, header: &Header) -> Result<ScanlineOutputFile> {
        let c_path = CString::new(path.to_str()
                                      .expect("non-unicode path handling is unimplemented")
                                      .as_bytes())
                .unwrap();
        let mut error_out = ptr::null();
        let mut out = ptr::null_mut();
        let error = unsafe {
            // NOTE: we don't need to keep a copy of the header, because this
            // function makes a deep copy that is stored in the CEXR_OutputFile.
            CEXR_OutputFile_from_file(c_path.as_ptr(), header.handle, 1, &mut out, &mut error_out)
        };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            Err(Error::Generic(msg.to_string_lossy().into_owned()))
        } else {
            Ok(ScanlineOutputFile {
                   handle: out,
                   header_ref: Header {
                       // NOTE: We're casting to *mut here to satisfy the
                       // field's type, but importantly we only return a
                       // const & of the Header so it retains const semantics.
                       handle: unsafe { CEXR_OutputFile_header(out) } as *mut CEXR_Header,
                       owned: false,
                       _phantom: PhantomData,
                   },
                   _phantom: PhantomData,
               })
        }
    }

    pub fn write_pixels(&mut self, framebuffer: &mut FrameBuffer) -> Result<()> {
        framebuffer.validate_header_for_output(self.header())?;

        let mut error_out = ptr::null();

        let error = unsafe {
            CEXR_OutputFile_set_framebuffer(self.handle, framebuffer.handle(), &mut error_out)
        };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            return Err(Error::Generic(msg.to_string_lossy().into_owned()));
        }

        let error = unsafe {
            CEXR_OutputFile_write_pixels(self.handle,
                                         framebuffer.dimensions().1 as i32,
                                         &mut error_out)
        };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            Err(Error::Generic(msg.to_string_lossy().into_owned()))
        } else {
            Ok(())
        }
    }

    pub fn header(&self) -> &Header {
        &self.header_ref
    }
}

impl Drop for ScanlineOutputFile {
    fn drop(&mut self) {
        unsafe { CEXR_OutputFile_delete(self.handle) };
    }
}
<commit_msg>Restore lifetime parameter to ScanlineOutputFile<commit_after>use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::path::Path;
use std::ptr;

use openexr_sys::*;

use error::*;
use frame_buffer::FrameBuffer;
use Header;

pub struct ScanlineOutputFile<'a> {
    handle: *mut CEXR_OutputFile,
    header_ref: Header,
    _phantom_1: PhantomData<CEXR_OutputFile>,
    _phantom_2: PhantomData<&'a mut [u8]>,
}

impl<'a> ScanlineOutputFile<'a> {
    pub fn new(path: &Path, header: &Header) -> Result<ScanlineOutputFile<'static>> {
        let c_path = CString::new(path.to_str()
                                      .expect("non-unicode path handling is unimplemented")
                                      .as_bytes())
                .unwrap();
        let mut error_out = ptr::null();
        let mut out = ptr::null_mut();
        let error = unsafe {
            // NOTE: we don't need to keep a copy of the header, because this
            // function makes a deep copy that is stored in the CEXR_OutputFile.
            CEXR_OutputFile_from_file(c_path.as_ptr(), header.handle, 1, &mut out, &mut error_out)
        };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            Err(Error::Generic(msg.to_string_lossy().into_owned()))
        } else {
            Ok(ScanlineOutputFile {
                   handle: out,
                   header_ref: Header {
                       // NOTE: We're casting to *mut here to satisfy the
                       // field's type, but importantly we only return a
                       // const & of the Header so it retains const semantics.
                       handle: unsafe { CEXR_OutputFile_header(out) } as *mut CEXR_Header,
                       owned: false,
                       _phantom: PhantomData,
                   },
                _phantom_1: PhantomData,
                _phantom_2: PhantomData,
               })
        }
    }

    pub fn write_pixels(&mut self, framebuffer: &mut FrameBuffer) -> Result<()> {
        framebuffer.validate_header_for_output(self.header())?;

        let mut error_out = ptr::null();

        let error = unsafe {
            CEXR_OutputFile_set_framebuffer(self.handle, framebuffer.handle(), &mut error_out)
        };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            return Err(Error::Generic(msg.to_string_lossy().into_owned()));
        }

        let error = unsafe {
            CEXR_OutputFile_write_pixels(self.handle,
                                         framebuffer.dimensions().1 as i32,
                                         &mut error_out)
        };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            Err(Error::Generic(msg.to_string_lossy().into_owned()))
        } else {
            Ok(())
        }
    }

    pub fn header(&self) -> &Header {
        &self.header_ref
    }
}

impl<'a> Drop for ScanlineOutputFile<'a> {
    fn drop(&mut self) {
        unsafe { CEXR_OutputFile_delete(self.handle) };
    }
}
<|endoftext|>


<commit_before>extern crate test;

use super::*;
use crate::graph::tests::TestGraph;

#[test]
fn diamond() {
    let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]);
    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), 4);
    assert_eq!(sccs.num_sccs(), 4);
}

#[test]
fn test_big_scc() {
    // The order in which things will be visited is important to this
    // test.
    //
    // We will visit:
    //
    // 0 -> 1 -> 2 -> 0
    //
    // and at this point detect a cycle. 2 will return back to 1 which
    // will visit 3. 3 will visit 2 before the cycle is complete, and
    // hence it too will return a cycle.

    /*
    +-> 0
    |   |
    |   v
    |   1 -> 3
    |   |    |
    |   v    |
    +-- 2 <--+
         */
    let graph = TestGraph::new(0, &[(0, 1), (1, 2), (1, 3), (2, 0), (3, 2)]);
    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), 1);
}

#[test]
fn test_three_sccs() {
    /*
        0
        |
        v
    +-> 1    3
    |   |    |
    |   v    |
    +-- 2 <--+
         */
    let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 1), (3, 2)]);
    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), 3);
    assert_eq!(sccs.scc(0), 1);
    assert_eq!(sccs.scc(1), 0);
    assert_eq!(sccs.scc(2), 0);
    assert_eq!(sccs.scc(3), 2);
    assert_eq!(sccs.successors(0), &[]);
    assert_eq!(sccs.successors(1), &[0]);
    assert_eq!(sccs.successors(2), &[0]);
}

#[test]
fn test_find_state_2() {
    // The order in which things will be visited is important to this
    // test. It tests part of the `find_state` behavior. Here is the
    // graph:
    //
    //
    //       /----+
    //     0 <--+ |
    //     |    | |
    //     v    | |
    // +-> 1 -> 3 4
    // |   |      |
    // |   v      |
    // +-- 2 <----+

    let graph = TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2)]);

    // For this graph, we will start in our DFS by visiting:
    //
    // 0 -> 1 -> 2 -> 1
    //
    // and at this point detect a cycle. The state of 2 will thus be
    // `InCycleWith { 1 }`.  We will then visit the 1 -> 3 edge, which
    // will attempt to visit 0 as well, thus going to the state
    // `InCycleWith { 0 }`. Finally, node 1 will complete; the lowest
    // depth of any successor was 3 which had depth 0, and thus it
    // will be in the state `InCycleWith { 3 }`.
    //
    // When we finally traverse the `0 -> 4` edge and then visit node 2,
    // the states of the nodes are:
    //
    // 0 BeingVisited { 0 }
    // 1 InCycleWith { 3 }
    // 2 InCycleWith { 1 }
    // 3 InCycleWith { 0 }
    //
    // and hence 4 will traverse the links, finding an ultimate depth of 0.
    // If will also collapse the states to the following:
    //
    // 0 BeingVisited { 0 }
    // 1 InCycleWith { 3 }
    // 2 InCycleWith { 1 }
    // 3 InCycleWith { 0 }

    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), 1);
    assert_eq!(sccs.scc(0), 0);
    assert_eq!(sccs.scc(1), 0);
    assert_eq!(sccs.scc(2), 0);
    assert_eq!(sccs.scc(3), 0);
    assert_eq!(sccs.scc(4), 0);
    assert_eq!(sccs.successors(0), &[]);
}

#[test]
fn test_find_state_3() {
    /*
          /----+
        0 <--+ |
        |    | |
        v    | |
    +-> 1 -> 3 4 5
    |   |      | |
    |   v      | |
    +-- 2 <----+-+
         */
    let graph =
        TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2), (5, 2)]);
    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), 2);
    assert_eq!(sccs.scc(0), 0);
    assert_eq!(sccs.scc(1), 0);
    assert_eq!(sccs.scc(2), 0);
    assert_eq!(sccs.scc(3), 0);
    assert_eq!(sccs.scc(4), 0);
    assert_eq!(sccs.scc(5), 1);
    assert_eq!(sccs.successors(0), &[]);
    assert_eq!(sccs.successors(1), &[0]);
}

#[bench]
fn bench_sccc(b: &mut test::Bencher) {
    // Like `test_three_sccs` but each state is replaced by a group of
    // three or four to have some amount of test data.
    /*
       0-3
        |
        v
    +->4-6 11-14
    |   |    |
    |   v    |
    +--7-10<-+
         */
    fn make_3_clique(slice: &mut [(usize, usize)], base: usize) {
        slice[0] = (base + 0, base + 1);
        slice[1] = (base + 1, base + 2);
        slice[2] = (base + 2, base + 0);
    }
    // Not actually a clique but strongly connected.
    fn make_4_clique(slice: &mut [(usize, usize)], base: usize) {
        slice[0] = (base + 0, base + 1);
        slice[1] = (base + 1, base + 2);
        slice[2] = (base + 2, base + 3);
        slice[3] = (base + 3, base + 0);
        slice[4] = (base + 1, base + 3);
        slice[5] = (base + 2, base + 1);
    }

    let mut graph = [(0, 0); 6 + 3 + 6 + 3 + 4];
    make_4_clique(&mut graph[0..6], 0);
    make_3_clique(&mut graph[6..9], 4);
    make_4_clique(&mut graph[9..15], 7);
    make_3_clique(&mut graph[15..18], 11);
    graph[18] = (0, 4);
    graph[19] = (5, 7);
    graph[20] = (11, 10);
    graph[21] = (7, 4);
    let graph = TestGraph::new(0, &graph[..]);
    b.iter(|| {
        let sccs: Sccs<_, usize> = Sccs::new(&graph);
        assert_eq!(sccs.num_sccs(), 3);
    });
}
<commit_msg>Add test for sccc of a long list<commit_after>extern crate test;

use super::*;
use crate::graph::tests::TestGraph;

#[test]
fn diamond() {
    let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]);
    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), 4);
    assert_eq!(sccs.num_sccs(), 4);
}

#[test]
fn test_big_scc() {
    // The order in which things will be visited is important to this
    // test.
    //
    // We will visit:
    //
    // 0 -> 1 -> 2 -> 0
    //
    // and at this point detect a cycle. 2 will return back to 1 which
    // will visit 3. 3 will visit 2 before the cycle is complete, and
    // hence it too will return a cycle.

    /*
    +-> 0
    |   |
    |   v
    |   1 -> 3
    |   |    |
    |   v    |
    +-- 2 <--+
         */
    let graph = TestGraph::new(0, &[(0, 1), (1, 2), (1, 3), (2, 0), (3, 2)]);
    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), 1);
}

#[test]
fn test_three_sccs() {
    /*
        0
        |
        v
    +-> 1    3
    |   |    |
    |   v    |
    +-- 2 <--+
         */
    let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 1), (3, 2)]);
    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), 3);
    assert_eq!(sccs.scc(0), 1);
    assert_eq!(sccs.scc(1), 0);
    assert_eq!(sccs.scc(2), 0);
    assert_eq!(sccs.scc(3), 2);
    assert_eq!(sccs.successors(0), &[]);
    assert_eq!(sccs.successors(1), &[0]);
    assert_eq!(sccs.successors(2), &[0]);
}

#[test]
fn test_find_state_2() {
    // The order in which things will be visited is important to this
    // test. It tests part of the `find_state` behavior. Here is the
    // graph:
    //
    //
    //       /----+
    //     0 <--+ |
    //     |    | |
    //     v    | |
    // +-> 1 -> 3 4
    // |   |      |
    // |   v      |
    // +-- 2 <----+

    let graph = TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2)]);

    // For this graph, we will start in our DFS by visiting:
    //
    // 0 -> 1 -> 2 -> 1
    //
    // and at this point detect a cycle. The state of 2 will thus be
    // `InCycleWith { 1 }`.  We will then visit the 1 -> 3 edge, which
    // will attempt to visit 0 as well, thus going to the state
    // `InCycleWith { 0 }`. Finally, node 1 will complete; the lowest
    // depth of any successor was 3 which had depth 0, and thus it
    // will be in the state `InCycleWith { 3 }`.
    //
    // When we finally traverse the `0 -> 4` edge and then visit node 2,
    // the states of the nodes are:
    //
    // 0 BeingVisited { 0 }
    // 1 InCycleWith { 3 }
    // 2 InCycleWith { 1 }
    // 3 InCycleWith { 0 }
    //
    // and hence 4 will traverse the links, finding an ultimate depth of 0.
    // If will also collapse the states to the following:
    //
    // 0 BeingVisited { 0 }
    // 1 InCycleWith { 3 }
    // 2 InCycleWith { 1 }
    // 3 InCycleWith { 0 }

    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), 1);
    assert_eq!(sccs.scc(0), 0);
    assert_eq!(sccs.scc(1), 0);
    assert_eq!(sccs.scc(2), 0);
    assert_eq!(sccs.scc(3), 0);
    assert_eq!(sccs.scc(4), 0);
    assert_eq!(sccs.successors(0), &[]);
}

#[test]
fn test_find_state_3() {
    /*
          /----+
        0 <--+ |
        |    | |
        v    | |
    +-> 1 -> 3 4 5
    |   |      | |
    |   v      | |
    +-- 2 <----+-+
         */
    let graph =
        TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2), (5, 2)]);
    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), 2);
    assert_eq!(sccs.scc(0), 0);
    assert_eq!(sccs.scc(1), 0);
    assert_eq!(sccs.scc(2), 0);
    assert_eq!(sccs.scc(3), 0);
    assert_eq!(sccs.scc(4), 0);
    assert_eq!(sccs.scc(5), 1);
    assert_eq!(sccs.successors(0), &[]);
    assert_eq!(sccs.successors(1), &[0]);
}

#[test]
fn test_deep_linear() {
    /*
    0
    |
    v
    1
    |
    v
    2
    |
    v
    …
     */
    const NR_NODES: usize = 1 << 14;
    let mut nodes = vec![];
    for i in 1..NR_NODES {
        nodes.push((i - 1, i));
    }
    let graph = TestGraph::new(0, nodes.as_slice());
    let sccs: Sccs<_, usize> = Sccs::new(&graph);
    assert_eq!(sccs.num_sccs(), NR_NODES);
    assert_eq!(sccs.scc(0), NR_NODES - 1);
    assert_eq!(sccs.scc(NR_NODES - 1), 0);
}

#[bench]
fn bench_sccc(b: &mut test::Bencher) {
    // Like `test_three_sccs` but each state is replaced by a group of
    // three or four to have some amount of test data.
    /*
       0-3
        |
        v
    +->4-6 11-14
    |   |    |
    |   v    |
    +--7-10<-+
         */
    fn make_3_clique(slice: &mut [(usize, usize)], base: usize) {
        slice[0] = (base + 0, base + 1);
        slice[1] = (base + 1, base + 2);
        slice[2] = (base + 2, base + 0);
    }
    // Not actually a clique but strongly connected.
    fn make_4_clique(slice: &mut [(usize, usize)], base: usize) {
        slice[0] = (base + 0, base + 1);
        slice[1] = (base + 1, base + 2);
        slice[2] = (base + 2, base + 3);
        slice[3] = (base + 3, base + 0);
        slice[4] = (base + 1, base + 3);
        slice[5] = (base + 2, base + 1);
    }

    let mut graph = [(0, 0); 6 + 3 + 6 + 3 + 4];
    make_4_clique(&mut graph[0..6], 0);
    make_3_clique(&mut graph[6..9], 4);
    make_4_clique(&mut graph[9..15], 7);
    make_3_clique(&mut graph[15..18], 11);
    graph[18] = (0, 4);
    graph[19] = (5, 7);
    graph[20] = (11, 10);
    graph[21] = (7, 4);
    let graph = TestGraph::new(0, &graph[..]);
    b.iter(|| {
        let sccs: Sccs<_, usize> = Sccs::new(&graph);
        assert_eq!(sccs.num_sccs(), 3);
    });
}
<|endoftext|>


<commit_before>use store::FileLockEntry;
use storeid::StoreId;
use hook::Hook;
use hook::result::HookResult;
use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor};
use hook::accessor::HookDataAccessor as HDA;

use hook::error::HookError as HE;
use hook::error::HookErrorKind as HEK;

#[derive(Debug)]
pub struct Aspect {
    name: String,
    hooks: Vec<Box<Hook>>,
}

impl Aspect {

    pub fn new(name: String) -> Aspect {
        Aspect {
            name: name,
            hooks: vec![],
        }
    }

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn register_hook(&mut self, h: Box<Hook>) {
        self.hooks.push(h);
    }

}

impl StoreIdAccessor for Aspect {
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use crossbeam;
        use std::thread;
        use std::thread::JoinHandle;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::StoreIdAccess(_)  => true, _ => false }) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::StoreIdAccess(accessor) => accessor.access(id),
                            _ => unreachable!(),
                        }
                        .map_err(|e| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

impl MutableHookDataAccessor for Aspect {
    fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::MutableAccess(_)  => true, _ => false }) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        for accessor in accessors {
            match accessor {
                HDA::MutableAccess(accessor) => try!(accessor.access_mut(fle)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl NonMutableHookDataAccessor for Aspect {
    fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
        use crossbeam;
        use std::thread;
        use std::thread::JoinHandle;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::NonMutableAccess(_)  => true, _ => false }) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::NonMutableAccess(accessor) => accessor.access(fle),
                            _ => unreachable!(),
                        }
                        .map_err(|e| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

<commit_msg>Aspect: Optionally get configuration<commit_after>use store::FileLockEntry;
use storeid::StoreId;
use hook::Hook;
use hook::result::HookResult;
use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor};
use hook::accessor::HookDataAccessor as HDA;

use hook::error::HookError as HE;
use hook::error::HookErrorKind as HEK;
use configuration::AspectConfig;

#[derive(Debug)]
pub struct Aspect {
    cfg: Option<AspectConfig>,
    name: String,
    hooks: Vec<Box<Hook>>,
}

impl Aspect {

    pub fn new(name: String, cfg: Option<AspectConfig>) -> Aspect {
        Aspect {
            cfg: cfg,
            name: name,
            hooks: vec![],
        }
    }

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn register_hook(&mut self, h: Box<Hook>) {
        self.hooks.push(h);
    }

}

impl StoreIdAccessor for Aspect {
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use crossbeam;
        use std::thread;
        use std::thread::JoinHandle;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::StoreIdAccess(_)  => true, _ => false }) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::StoreIdAccess(accessor) => accessor.access(id),
                            _ => unreachable!(),
                        }
                        .map_err(|e| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

impl MutableHookDataAccessor for Aspect {
    fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::MutableAccess(_)  => true, _ => false }) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        for accessor in accessors {
            match accessor {
                HDA::MutableAccess(accessor) => try!(accessor.access_mut(fle)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl NonMutableHookDataAccessor for Aspect {
    fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
        use crossbeam;
        use std::thread;
        use std::thread::JoinHandle;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::NonMutableAccess(_)  => true, _ => false }) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::NonMutableAccess(accessor) => accessor.access(fle),
                            _ => unreachable!(),
                        }
                        .map_err(|e| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

<|endoftext|>