<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use channel::libra_channel::{self, Receiver};
use futures::{sink::SinkExt, StreamExt};
use libra_config::{config::RoleType, network_id::NetworkContext};
use libra_crypto::x25519;
use libra_logger::prelude::*;
use libra_metrics::{register_histogram, DurationHistogram};
use libra_network_address::NetworkAddress;
use libra_network_address_encryption::{Encryptor, Error as EncryptorError};
use libra_types::on_chain_config::{OnChainConfigPayload, ValidatorSet, ON_CHAIN_CONFIG_REGISTRY};
use network::{
    connectivity_manager::{ConnectivityRequest, DiscoverySource},
    logging::NetworkSchema,
};
use once_cell::sync::Lazy;
use std::{
    collections::{HashMap, HashSet},
    sync::Arc,
    time::Instant,
};
use subscription_service::ReconfigSubscription;

pub mod builder;

/// Histogram of idle time of spent in event processing loop
pub static EVENT_PROCESSING_LOOP_IDLE_DURATION_S: Lazy<DurationHistogram> = Lazy::new(|| {
    DurationHistogram::new(
        register_histogram!(
            "simple_onchain_discovery_event_processing_loop_idle_duration_s",
            "Histogram of idle time of spent in event processing loop"
        )
        .unwrap(),
    )
});

/// Histogram of busy time of spent in event processing loop
pub static EVENT_PROCESSING_LOOP_BUSY_DURATION_S: Lazy<DurationHistogram> = Lazy::new(|| {
    DurationHistogram::new(
        register_histogram!(
            "simple_onchain_discovery_event_processing_loop_busy_duration_s",
            "Histogram of busy time of spent in event processing loop"
        )
        .unwrap(),
    )
});

/// Listener which converts published  updates from the OnChainConfig to ConnectivityRequests
/// for the ConnectivityManager.
pub struct ConfigurationChangeListener {
    network_context: Arc<NetworkContext>,
    encryptor: Encryptor,
    conn_mgr_reqs_tx: channel::Sender<ConnectivityRequest>,
    reconfig_events: libra_channel::Receiver<(), OnChainConfigPayload>,
}

pub fn gen_simple_discovery_reconfig_subscription(
) -> (ReconfigSubscription, Receiver<(), OnChainConfigPayload>) {
    ReconfigSubscription::subscribe_all("network", ON_CHAIN_CONFIG_REGISTRY.to_vec(), vec![])
}

/// Extracts a set of ConnectivityRequests from a ValidatorSet which are appropriate for a network with type role.
fn extract_updates(
    network_context: Arc<NetworkContext>,
    encryptor: &Encryptor,
    node_set: ValidatorSet,
) -> Vec<ConnectivityRequest> {
    let role = network_context.role();

    // Decode addresses while ignoring bad addresses
    let new_peer_addrs: HashMap<_, _> = node_set
        .into_iter()
        .map(|info| {
            let peer_id = *info.account_address();
            let config = info.into_config();

            let addrs_res = match role {
                RoleType::Validator => {
                    let result = encryptor.decrypt(&config.validator_network_addresses, peer_id);
                    if let Err(EncryptorError::StorageError(_)) = result {
                        panic!(format!(
                            "Unable to initialize validator network addresses: {:?}",
                            result
                        ));
                    }
                    result.map_err(anyhow::Error::from)
                }
                RoleType::FullNode => config
                    .fullnode_network_addresses()
                    .map_err(anyhow::Error::from),
            };

            let addrs = match addrs_res {
                Ok(addrs) => addrs,
                Err(err) => {
                    warn!(
                        NetworkSchema::new(&network_context),
                        "OnChainDiscovery: Failed to parse any network address: peer: {}, err: {}",
                        peer_id,
                        err
                    );
                    Vec::new()
                }
            };

            (peer_id, addrs)
        })
        .collect();

    // Retrieve public keys from addresses
    let new_peer_pubkeys: HashMap<_, _> = new_peer_addrs
        .iter()
        .map(|(peer_id, addrs)| {
            let pubkeys: HashSet<x25519::PublicKey> = addrs
                .iter()
                .filter_map(NetworkAddress::find_noise_proto)
                .collect();
            (*peer_id, pubkeys)
        })
        .collect();

    vec![
        ConnectivityRequest::UpdateAddresses(DiscoverySource::OnChain, new_peer_addrs),
        ConnectivityRequest::UpdateEligibleNodes(DiscoverySource::OnChain, new_peer_pubkeys),
    ]
}

impl ConfigurationChangeListener {
    /// Creates a new ConfigurationListener
    pub fn new(
        network_context: Arc<NetworkContext>,
        encryptor: Encryptor,
        conn_mgr_reqs_tx: channel::Sender<ConnectivityRequest>,
        reconfig_events: libra_channel::Receiver<(), OnChainConfigPayload>,
    ) -> Self {
        Self {
            network_context,
            encryptor,
            conn_mgr_reqs_tx,
            reconfig_events,
        }
    }

    /// Processes a received OnChainConfigPayload.  Depending on role (Validator or FullNode), parses
    /// the appropriate configuration changes and passes it to the ConnectionManager channel.
    async fn process_payload(&mut self, payload: OnChainConfigPayload) {
        let node_set: ValidatorSet = payload
            .get()
            .expect("failed to get ValidatorSet from payload");

        let updates = extract_updates(self.network_context.clone(), &self.encryptor, node_set);

        info!(
            NetworkSchema::new(&self.network_context),
            "Update {} Network about new Node IDs",
            self.network_context.role()
        );

        for update in updates {
            match self.conn_mgr_reqs_tx.send(update).await {
                Ok(()) => (),
                Err(e) => warn!(
                    NetworkSchema::new(&self.network_context),
                    "Failed to send update to ConnectivityManager {}", e
                ),
            }
        }
    }

    /// Starts the listener to wait on reconfiguration events.  Creates an infinite loop.
    pub async fn start(mut self) {
        info!(
            NetworkSchema::new(&self.network_context),
            "{} Starting OnChain Discovery actor", self.network_context
        );
        loop {
            let start_idle_time = Instant::now();
            let payload = self.reconfig_events.select_next_some().await;
            let idle_duration = start_idle_time.elapsed();
            let start_process_time = Instant::now();
            self.process_payload(payload).await;
            let process_duration = start_process_time.elapsed();

            EVENT_PROCESSING_LOOP_IDLE_DURATION_S.observe_duration(idle_duration);
            EVENT_PROCESSING_LOOP_BUSY_DURATION_S.observe_duration(process_duration);
        }
    }
}
<commit_msg>[network] add on chain discovery metrics<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use channel::libra_channel::{self, Receiver};
use futures::{sink::SinkExt, StreamExt};
use libra_config::{config::RoleType, network_id::NetworkContext};
use libra_crypto::x25519;
use libra_logger::prelude::*;
use libra_metrics::{
    register_histogram, register_int_counter_vec, DurationHistogram, IntCounterVec,
};
use libra_network_address::NetworkAddress;
use libra_network_address_encryption::{Encryptor, Error as EncryptorError};
use libra_types::on_chain_config::{OnChainConfigPayload, ValidatorSet, ON_CHAIN_CONFIG_REGISTRY};
use network::{
    connectivity_manager::{ConnectivityRequest, DiscoverySource},
    counters::inc_by_with_context,
    logging::NetworkSchema,
};
use once_cell::sync::Lazy;
use std::{
    collections::{HashMap, HashSet},
    sync::Arc,
    time::Instant,
};
use subscription_service::ReconfigSubscription;

pub mod builder;

/// Histogram of idle time of spent in event processing loop
pub static EVENT_PROCESSING_LOOP_IDLE_DURATION_S: Lazy<DurationHistogram> = Lazy::new(|| {
    DurationHistogram::new(
        register_histogram!(
            "simple_onchain_discovery_event_processing_loop_idle_duration_s",
            "Histogram of idle time of spent in event processing loop"
        )
        .unwrap(),
    )
});

/// Histogram of busy time of spent in event processing loop
pub static EVENT_PROCESSING_LOOP_BUSY_DURATION_S: Lazy<DurationHistogram> = Lazy::new(|| {
    DurationHistogram::new(
        register_histogram!(
            "simple_onchain_discovery_event_processing_loop_busy_duration_s",
            "Histogram of busy time of spent in event processing loop"
        )
        .unwrap(),
    )
});

pub static DISCOVERY_COUNTS: Lazy<IntCounterVec> = Lazy::new(|| {
    register_int_counter_vec!(
        "libra_simple_onchain_discovery_counts",
        "Histogram of busy time of spent in event processing loop",
        &["role_type", "network_id", "peer_id", "metric"]
    )
    .unwrap()
});

/// Listener which converts published  updates from the OnChainConfig to ConnectivityRequests
/// for the ConnectivityManager.
pub struct ConfigurationChangeListener {
    network_context: Arc<NetworkContext>,
    encryptor: Encryptor,
    conn_mgr_reqs_tx: channel::Sender<ConnectivityRequest>,
    reconfig_events: libra_channel::Receiver<(), OnChainConfigPayload>,
}

pub fn gen_simple_discovery_reconfig_subscription(
) -> (ReconfigSubscription, Receiver<(), OnChainConfigPayload>) {
    ReconfigSubscription::subscribe_all("network", ON_CHAIN_CONFIG_REGISTRY.to_vec(), vec![])
}

/// Extracts a set of ConnectivityRequests from a ValidatorSet which are appropriate for a network with type role.
fn extract_updates(
    network_context: Arc<NetworkContext>,
    encryptor: &Encryptor,
    node_set: ValidatorSet,
) -> Vec<ConnectivityRequest> {
    let role = network_context.role();

    // Decode addresses while ignoring bad addresses
    let new_peer_addrs: HashMap<_, _> = node_set
        .into_iter()
        .map(|info| {
            let peer_id = *info.account_address();
            let config = info.into_config();

            let addrs = match role {
                RoleType::Validator => {
                    let result = encryptor.decrypt(&config.validator_network_addresses, peer_id);
                    if let Err(EncryptorError::StorageError(_)) = result {
                        panic!(format!(
                            "Unable to initialize validator network addresses: {:?}",
                            result
                        ));
                    }
                    result.map_err(anyhow::Error::from)
                }
                RoleType::FullNode => config
                    .fullnode_network_addresses()
                    .map_err(anyhow::Error::from),
            }
            .map_err(|err| {
                inc_by_with_context(&DISCOVERY_COUNTS, &network_context, "read_failure", 1);

                warn!(
                    NetworkSchema::new(&network_context),
                    "OnChainDiscovery: Failed to parse any network address: peer: {}, err: {}",
                    peer_id,
                    err
                )
            })
            .unwrap_or_default();

            (peer_id, addrs)
        })
        .collect();

    // Retrieve public keys from addresses
    let new_peer_pubkeys: HashMap<_, _> = new_peer_addrs
        .iter()
        .map(|(peer_id, addrs)| {
            let pubkeys: HashSet<x25519::PublicKey> = addrs
                .iter()
                .filter_map(NetworkAddress::find_noise_proto)
                .collect();
            (*peer_id, pubkeys)
        })
        .collect();

    vec![
        ConnectivityRequest::UpdateAddresses(DiscoverySource::OnChain, new_peer_addrs),
        ConnectivityRequest::UpdateEligibleNodes(DiscoverySource::OnChain, new_peer_pubkeys),
    ]
}

impl ConfigurationChangeListener {
    /// Creates a new ConfigurationListener
    pub fn new(
        network_context: Arc<NetworkContext>,
        encryptor: Encryptor,
        conn_mgr_reqs_tx: channel::Sender<ConnectivityRequest>,
        reconfig_events: libra_channel::Receiver<(), OnChainConfigPayload>,
    ) -> Self {
        Self {
            network_context,
            encryptor,
            conn_mgr_reqs_tx,
            reconfig_events,
        }
    }

    /// Processes a received OnChainConfigPayload.  Depending on role (Validator or FullNode), parses
    /// the appropriate configuration changes and passes it to the ConnectionManager channel.
    async fn process_payload(&mut self, payload: OnChainConfigPayload) {
        let node_set: ValidatorSet = payload
            .get()
            .expect("failed to get ValidatorSet from payload");

        let updates = extract_updates(self.network_context.clone(), &self.encryptor, node_set);

        inc_by_with_context(
            &DISCOVERY_COUNTS,
            &self.network_context,
            "new_nodes",
            updates.len() as i64,
        );
        info!(
            NetworkSchema::new(&self.network_context),
            "Update {} Network about new Node IDs",
            self.network_context.role()
        );

        for update in updates {
            match self.conn_mgr_reqs_tx.send(update).await {
                Ok(()) => (),
                Err(e) => {
                    inc_by_with_context(
                        &DISCOVERY_COUNTS,
                        &self.network_context,
                        "send_failure",
                        1,
                    );
                    warn!(
                        NetworkSchema::new(&self.network_context),
                        "Failed to send update to ConnectivityManager {}", e
                    )
                }
            }
        }
    }

    /// Starts the listener to wait on reconfiguration events.  Creates an infinite loop.
    pub async fn start(mut self) {
        info!(
            NetworkSchema::new(&self.network_context),
            "{} Starting OnChain Discovery actor", self.network_context
        );
        loop {
            let start_idle_time = Instant::now();
            let payload = self.reconfig_events.select_next_some().await;
            let idle_duration = start_idle_time.elapsed();
            let start_process_time = Instant::now();
            self.process_payload(payload).await;
            let process_duration = start_process_time.elapsed();

            EVENT_PROCESSING_LOOP_IDLE_DURATION_S.observe_duration(idle_duration);
            EVENT_PROCESSING_LOOP_BUSY_DURATION_S.observe_duration(process_duration);
        }
    }
}
<|endoftext|>


<commit_before>//< Helpers that are useful for tests and doctests.

/// configure a global logger at level==DEBUG.
pub fn init_logging() {
    let log_level = log::LevelFilter::Debug;
    fern::Dispatch::new()
        .format(move |out, message, record| {
            out.finish(format_args!(
                "{} [{:5}] {} {}",
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                record.level(),
                if log_level == log::LevelFilter::Trace {
                    record.target()
                } else {
                    ""
                },
                message
            ))
        })
        .level(log_level)
        .chain(std::io::stderr())
        .filter(|metadata| !metadata.target().starts_with("goblin::pe"))
        .apply()
        .expect("failed to configure logging");
}
<commit_msg>test: add helpers to construct module from shellcode<commit_after>//< Helpers that are useful for tests and doctests.

use anyhow::Result;

use crate::aspace::RelativeAddressSpace;
use crate::module::{Arch, Module, Permissions, Section};
use crate::RVA;

/// configure a global logger at level==DEBUG.
pub fn init_logging() {
    let log_level = log::LevelFilter::Debug;
    fern::Dispatch::new()
        .format(move |out, message, record| {
            out.finish(format_args!(
                "{} [{:5}] {} {}",
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                record.level(),
                if log_level == log::LevelFilter::Trace {
                    record.target()
                } else {
                    ""
                },
                message
            ))
        })
        .level(log_level)
        .chain(std::io::stderr())
        .filter(|metadata| !metadata.target().starts_with("goblin::pe"))
        .apply()
        .expect("failed to configure logging");
}

pub fn load_shellcode(arch: Arch, buf: &[u8]) -> Result<Module> {
    let mut address_space = RelativeAddressSpace::with_capacity(buf.len() as u64);
    address_space.map.writezx(0x0, buf)?;

    Ok(Module {
        arch,
        sections: vec![Section {
            name: "shellcode".to_string(),
            perms: Permissions::RWX,
            physical_range: std::ops::Range {
                start: 0x0,
                end: buf.len() as RVA,
            },
            virtual_range: std::ops::Range {
                start: 0x0,
                end: buf.len() as RVA,
            },
        }],
        address_space: address_space.into_absolute(0x0)?,
    })
}

pub fn load_shellcode32(buf: &[u8]) -> Result<Module> {
    load_shellcode(Arch::X32, buf)
}

pub fn load_shellcode64(buf: &[u8]) -> Result<Module> {
    load_shellcode(Arch::X32, buf)
}
<|endoftext|>


<commit_before>//! General low-level receiver for all data units, covering frame synchronization up to
//! symbol decoding.

use baseband::decode::{Decoder, Decider};
use baseband::sync::{SyncCorrelator, SyncDetector, SymbolThresholds, sync_threshold};
use error::{P25Error, Result};
use message::nid;
use message::status::{StreamSymbol, StatusDeinterleaver};
use stats::{Stats, HasStats};

use self::State::*;
use self::StateChange::*;

/// Low-level receiver for decoding samples into symbols and deinterleaving status
/// symbols.
#[derive(Copy, Clone)]
struct SymbolReceiver {
    /// Symbol decoder.
    decoder: Decoder,
    /// Data/Status symbol deinterleaver.
    status: StatusDeinterleaver,
}

impl SymbolReceiver {
    /// Create a new `SymbolReceiver` using the given symbol decoder.
    pub fn new(decoder: Decoder) -> SymbolReceiver {
        SymbolReceiver {
            decoder: decoder,
            status: StatusDeinterleaver::new(),
        }
    }

    /// Feed in a baseband symbol, possibly producing a data or status symbol.
    pub fn feed(&mut self, s: f32) -> Option<StreamSymbol> {
        match self.decoder.feed(s) {
            Some(dibit) => Some(self.status.feed(dibit)),
            None => None,
        }
    }
}

/// An event seen by the low-level receiver.
#[derive(Debug)]
pub enum ReceiverEvent {
    /// Data or status symbol.
    Symbol(StreamSymbol),
    /// Decoded NID information.
    NetworkId(nid::NetworkId),
}

/// Internal state of the state machine.
enum State {
    /// Lock onto frame synchronization.
    Sync(SyncDetector),
    /// Decode NID.
    DecodeNID(SymbolReceiver, nid::NidReceiver),
    /// Decode data and status symbols.
    DecodePacket(SymbolReceiver),
    /// Flush pads at end of packet.
    FlushPads(SymbolReceiver),
}

/// Action the state machine should take.
enum StateChange {
    /// Change to the given state.
    Change(State),
    /// Propagate the given event.
    Event(ReceiverEvent),
    /// Change to the given state and propagate the given event.
    EventChange(ReceiverEvent, State),
    /// Propagate the given error.
    Error(P25Error),
    /// No action necessary.
    NoChange,
}

impl State {
    /// Initial synchronization state.
    pub fn sync() -> State { Sync(SyncDetector::new()) }

    /// Initial NID decode state.
    pub fn decode_nid(decoder: Decoder) -> State {
        DecodeNID(SymbolReceiver::new(decoder), nid::NidReceiver::new())
    }

    /// Initial symbol decode state.
    pub fn decode_packet(recv: SymbolReceiver) -> State { DecodePacket(recv) }

    /// Initial flush padding state.
    pub fn flush_pads(recv: SymbolReceiver) -> State { FlushPads(recv) }
}

/// State machine for low-level data unit reception.
///
/// The state machine consumes baseband samples and performs the following steps common to
/// all data units:
///
/// 1. Track average power of input signal
/// 2. Lock onto frame synchronization
/// 3. Deinterleave status symbols
/// 4. Decode NID information
/// 5. Decode dibit symbols until stopped
pub struct DataUnitReceiver {
    /// Current state.
    state: State,
    /// Tracks input signal power and frame synchronization statistics.
    corr: SyncCorrelator,
    /// Tracks thresholds for symbol decisions.
    symthresh: SymbolThresholds,
    stats: Stats,
}

impl DataUnitReceiver {
    /// Create a new `DataUnitReceiver` in the initial reception state.
    pub fn new() -> DataUnitReceiver {
        DataUnitReceiver {
            state: State::sync(),
            corr: SyncCorrelator::new(),
            symthresh: SymbolThresholds::new(),
            stats: Stats::default(),
        }
    }

    /// Flush any remaining padding symbols at the end of the current packet, and reenter
    /// the frame synchronization state afterwards.
    pub fn flush_pads(&mut self) {
        match self.state {
            DecodePacket(recv) => self.state = State::flush_pads(recv),
            Sync(_) => {},
            _ => panic!("not decoding a packet"),
        }
    }

    /// Force the receiver into frame synchronization.
    pub fn resync(&mut self) { self.state = State::sync(); }

    /// Determine the next action to take based on the given sample.
    fn handle(&mut self, s: f32) -> StateChange {
        // Continuously track the input signal power.
        let (corrpow, sigpow) = self.corr.feed(s);

        match self.state {
            Sync(ref mut sync) => if sync.detect(corrpow, sync_threshold(sigpow)) {
                let history = self.corr.history();
                let (p, m, n) = self.symthresh.thresholds(&history);

                println!("pos:{} mid:{} neg:{}", p, m, n);

                Change(State::decode_nid(Decoder::new(Decider::new(p, m, n))))
            } else {
                NoChange
            },
            DecodeNID(ref mut recv, ref mut nidrecv) => {
                let dibit = match recv.feed(s) {
                    Some(StreamSymbol::Data(d)) => d,
                    Some(s) => return Event(ReceiverEvent::Symbol(s)),
                    None => return NoChange,
                };

                match nidrecv.feed(dibit) {
                    Some(Ok(nid)) => {
                        self.stats.merge(nidrecv);
                        EventChange(ReceiverEvent::NetworkId(nid),
                                    State::decode_packet(*recv))
                    },
                    Some(Err(e)) => Error(e),
                    None => NoChange,
                }
            },
            DecodePacket(ref mut recv) => match recv.feed(s) {
                Some(x) => Event(ReceiverEvent::Symbol(x)),
                None => NoChange,
            },
            FlushPads(ref mut recv) => match recv.feed(s) {
                // According to the spec, the stream is padded until the next status
                // symbol boundary.
                Some(StreamSymbol::Status(_)) => Change(State::sync()),
                _ => NoChange,
            },
        }
    }

    /// Feed in a baseband symbol, possibly producing a receiver event. Return
    /// `Some(Ok(event))` for any normal event, `Some(Err(err))` for any error, and `None`
    /// if no event occurred.
    pub fn feed(&mut self, s: f32) -> Option<Result<ReceiverEvent>> {
        match self.handle(s) {
            Change(state) => {
                self.state = state;
                None
            },
            Event(event) => Some(Ok(event)),
            EventChange(event, state) => {
                self.state = state;
                Some(Ok(event))
            },
            Error(err) => Some(Err(err)),
            NoChange => None,
        }
    }
}

impl HasStats for DataUnitReceiver {
    fn stats(&mut self) -> &mut Stats { &mut self.stats }
}
<commit_msg>Remove leftover println<commit_after>//! General low-level receiver for all data units, covering frame synchronization up to
//! symbol decoding.

use baseband::decode::{Decoder, Decider};
use baseband::sync::{SyncCorrelator, SyncDetector, SymbolThresholds, sync_threshold};
use error::{P25Error, Result};
use message::nid;
use message::status::{StreamSymbol, StatusDeinterleaver};
use stats::{Stats, HasStats};

use self::State::*;
use self::StateChange::*;

/// Low-level receiver for decoding samples into symbols and deinterleaving status
/// symbols.
#[derive(Copy, Clone)]
struct SymbolReceiver {
    /// Symbol decoder.
    decoder: Decoder,
    /// Data/Status symbol deinterleaver.
    status: StatusDeinterleaver,
}

impl SymbolReceiver {
    /// Create a new `SymbolReceiver` using the given symbol decoder.
    pub fn new(decoder: Decoder) -> SymbolReceiver {
        SymbolReceiver {
            decoder: decoder,
            status: StatusDeinterleaver::new(),
        }
    }

    /// Feed in a baseband symbol, possibly producing a data or status symbol.
    pub fn feed(&mut self, s: f32) -> Option<StreamSymbol> {
        match self.decoder.feed(s) {
            Some(dibit) => Some(self.status.feed(dibit)),
            None => None,
        }
    }
}

/// An event seen by the low-level receiver.
#[derive(Debug)]
pub enum ReceiverEvent {
    /// Data or status symbol.
    Symbol(StreamSymbol),
    /// Decoded NID information.
    NetworkId(nid::NetworkId),
}

/// Internal state of the state machine.
enum State {
    /// Lock onto frame synchronization.
    Sync(SyncDetector),
    /// Decode NID.
    DecodeNID(SymbolReceiver, nid::NidReceiver),
    /// Decode data and status symbols.
    DecodePacket(SymbolReceiver),
    /// Flush pads at end of packet.
    FlushPads(SymbolReceiver),
}

/// Action the state machine should take.
enum StateChange {
    /// Change to the given state.
    Change(State),
    /// Propagate the given event.
    Event(ReceiverEvent),
    /// Change to the given state and propagate the given event.
    EventChange(ReceiverEvent, State),
    /// Propagate the given error.
    Error(P25Error),
    /// No action necessary.
    NoChange,
}

impl State {
    /// Initial synchronization state.
    pub fn sync() -> State { Sync(SyncDetector::new()) }

    /// Initial NID decode state.
    pub fn decode_nid(decoder: Decoder) -> State {
        DecodeNID(SymbolReceiver::new(decoder), nid::NidReceiver::new())
    }

    /// Initial symbol decode state.
    pub fn decode_packet(recv: SymbolReceiver) -> State { DecodePacket(recv) }

    /// Initial flush padding state.
    pub fn flush_pads(recv: SymbolReceiver) -> State { FlushPads(recv) }
}

/// State machine for low-level data unit reception.
///
/// The state machine consumes baseband samples and performs the following steps common to
/// all data units:
///
/// 1. Track average power of input signal
/// 2. Lock onto frame synchronization
/// 3. Deinterleave status symbols
/// 4. Decode NID information
/// 5. Decode dibit symbols until stopped
pub struct DataUnitReceiver {
    /// Current state.
    state: State,
    /// Tracks input signal power and frame synchronization statistics.
    corr: SyncCorrelator,
    /// Tracks thresholds for symbol decisions.
    symthresh: SymbolThresholds,
    stats: Stats,
}

impl DataUnitReceiver {
    /// Create a new `DataUnitReceiver` in the initial reception state.
    pub fn new() -> DataUnitReceiver {
        DataUnitReceiver {
            state: State::sync(),
            corr: SyncCorrelator::new(),
            symthresh: SymbolThresholds::new(),
            stats: Stats::default(),
        }
    }

    /// Flush any remaining padding symbols at the end of the current packet, and reenter
    /// the frame synchronization state afterwards.
    pub fn flush_pads(&mut self) {
        match self.state {
            DecodePacket(recv) => self.state = State::flush_pads(recv),
            Sync(_) => {},
            _ => panic!("not decoding a packet"),
        }
    }

    /// Force the receiver into frame synchronization.
    pub fn resync(&mut self) { self.state = State::sync(); }

    /// Determine the next action to take based on the given sample.
    fn handle(&mut self, s: f32) -> StateChange {
        // Continuously track the input signal power.
        let (corrpow, sigpow) = self.corr.feed(s);

        match self.state {
            Sync(ref mut sync) => if sync.detect(corrpow, sync_threshold(sigpow)) {
                let history = self.corr.history();
                let (p, m, n) = self.symthresh.thresholds(&history);

                Change(State::decode_nid(Decoder::new(Decider::new(p, m, n))))
            } else {
                NoChange
            },
            DecodeNID(ref mut recv, ref mut nidrecv) => {
                let dibit = match recv.feed(s) {
                    Some(StreamSymbol::Data(d)) => d,
                    Some(s) => return Event(ReceiverEvent::Symbol(s)),
                    None => return NoChange,
                };

                match nidrecv.feed(dibit) {
                    Some(Ok(nid)) => {
                        self.stats.merge(nidrecv);
                        EventChange(ReceiverEvent::NetworkId(nid),
                                    State::decode_packet(*recv))
                    },
                    Some(Err(e)) => Error(e),
                    None => NoChange,
                }
            },
            DecodePacket(ref mut recv) => match recv.feed(s) {
                Some(x) => Event(ReceiverEvent::Symbol(x)),
                None => NoChange,
            },
            FlushPads(ref mut recv) => match recv.feed(s) {
                // According to the spec, the stream is padded until the next status
                // symbol boundary.
                Some(StreamSymbol::Status(_)) => Change(State::sync()),
                _ => NoChange,
            },
        }
    }

    /// Feed in a baseband symbol, possibly producing a receiver event. Return
    /// `Some(Ok(event))` for any normal event, `Some(Err(err))` for any error, and `None`
    /// if no event occurred.
    pub fn feed(&mut self, s: f32) -> Option<Result<ReceiverEvent>> {
        match self.handle(s) {
            Change(state) => {
                self.state = state;
                None
            },
            Event(event) => Some(Ok(event)),
            EventChange(event, state) => {
                self.state = state;
                Some(Ok(event))
            },
            Error(err) => Some(Err(err)),
            NoChange => None,
        }
    }
}

impl HasStats for DataUnitReceiver {
    fn stats(&mut self) -> &mut Stats { &mut self.stats }
}
<|endoftext|>


<commit_before>use crate::cell::UnsafeCell;
use crate::collections::VecDeque;
use crate::ffi::c_void;
use crate::ops::{Deref, DerefMut, Drop};
use crate::ptr;
use crate::sync::atomic::{spin_loop_hint, AtomicUsize, Ordering};
use crate::sys::hermit::abi;

/// This type provides a lock based on busy waiting to realize mutual exclusion
///
/// # Description
///
/// This structure behaves a lot like a common mutex. There are some differences:
///
/// - By using busy waiting, it can be used outside the runtime.
/// - It is a so called ticket lock and is completly fair.
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
struct Spinlock<T: ?Sized> {
    queue: AtomicUsize,
    dequeue: AtomicUsize,
    data: UnsafeCell<T>,
}

unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
struct SpinlockGuard<'a, T: ?Sized + 'a> {
    dequeue: &'a AtomicUsize,
    data: &'a mut T,
}

impl<T> Spinlock<T> {
    pub const fn new(user_data: T) -> Spinlock<T> {
        SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() }
    }

    #[inline]
    fn obtain_lock(&self) {
        let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
        while self.dequeue.load(Ordering::SeqCst) != ticket {
            spin_loop_hint();
        }
    }

    #[inline]
    pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
        self.obtain_lock();
        SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() }
    }
}

impl<T: ?Sized + Default> Default for Spinlock<T> {
    fn default() -> Spinlock<T> {
        Spinlock::new(Default::default())
    }
}

impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        &*self.data
    }
}

impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut *self.data
    }
}

impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
    /// The dropping of the SpinlockGuard will release the lock it was created from.
    fn drop(&mut self) {
        self.dequeue.fetch_add(1, Ordering::SeqCst);
    }
}

/// Realize a priority queue for tasks
struct PriorityQueue {
    queues: [Option<VecDeque<abi::Tid>>; abi::NO_PRIORITIES],
    prio_bitmap: u64,
}

impl PriorityQueue {
    pub const fn new() -> PriorityQueue {
        PriorityQueue {
            queues: [
                None, None, None, None, None, None, None, None, None, None, None, None, None, None,
                None, None, None, None, None, None, None, None, None, None, None, None, None, None,
                None, None, None,
            ],
            prio_bitmap: 0,
        }
    }

    /// Add a task id by its priority to the queue
    pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) {
        let i: usize = prio.into().into();
        self.prio_bitmap |= (1 << i) as u64;
        if let Some(queue) = &mut self.queues[i] {
            queue.push_back(id);
        } else {
            let mut queue = VecDeque::new();
            queue.push_back(id);
            self.queues[i] = Some(queue);
        }
    }

    fn pop_from_queue(&mut self, queue_index: usize) -> Option<abi::Tid> {
        if let Some(queue) = &mut self.queues[queue_index] {
            let id = queue.pop_front();

            if queue.is_empty() {
                self.prio_bitmap &= !(1 << queue_index as u64);
            }

            id
        } else {
            None
        }
    }

    /// Pop the task handle with the highest priority from the queue
    pub fn pop(&mut self) -> Option<abi::Tid> {
        for i in 0..abi::NO_PRIORITIES {
            if self.prio_bitmap & (1 << i) != 0 {
                return self.pop_from_queue(i);
            }
        }

        None
    }
}

struct MutexInner {
    locked: bool,
    blocked_task: PriorityQueue,
}

impl MutexInner {
    pub const fn new() -> MutexInner {
        MutexInner { locked: false, blocked_task: PriorityQueue::new() }
    }
}

pub struct Mutex {
    inner: Spinlock<MutexInner>,
}

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

impl Mutex {
    pub const fn new() -> Mutex {
        Mutex { inner: Spinlock::new(MutexInner::new()) }
    }

    #[inline]
    pub unsafe fn init(&mut self) {
        self.inner = Spinlock::new(MutexInner::new());
    }

    #[inline]
    pub unsafe fn lock(&self) {
        loop {
            let mut guard = self.inner.lock();
            if guard.locked == false {
                guard.locked = true;
                return;
            } else {
                let prio = abi::get_priority();
                let id = abi::getpid();

                guard.blocked_task.push(prio, id);
                abi::block_current_task();
                drop(guard);
                abi::yield_now();
            }
        }
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        let mut guard = self.inner.lock();
        guard.locked = false;
        if let Some(tid) = guard.blocked_task.pop() {
            abi::wakeup_task(tid);
        }
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        let mut guard = self.inner.lock();
        if guard.locked == false {
            guard.locked = true;
        }
        guard.locked
    }

    #[inline]
    pub unsafe fn destroy(&self) {}
}

pub struct ReentrantMutex {
    inner: *const c_void,
}

impl ReentrantMutex {
    pub const unsafe fn uninitialized() -> ReentrantMutex {
        ReentrantMutex { inner: ptr::null() }
    }

    #[inline]
    pub unsafe fn init(&self) {
        let _ = abi::recmutex_init(&self.inner as *const *const c_void as *mut _);
    }

    #[inline]
    pub unsafe fn lock(&self) {
        let _ = abi::recmutex_lock(self.inner);
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        true
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        let _ = abi::recmutex_unlock(self.inner);
    }

    #[inline]
    pub unsafe fn destroy(&self) {
        let _ = abi::recmutex_destroy(self.inner);
    }
}
<commit_msg>fix typos in new method<commit_after>use crate::cell::UnsafeCell;
use crate::collections::VecDeque;
use crate::ffi::c_void;
use crate::ops::{Deref, DerefMut, Drop};
use crate::ptr;
use crate::sync::atomic::{spin_loop_hint, AtomicUsize, Ordering};
use crate::sys::hermit::abi;

/// This type provides a lock based on busy waiting to realize mutual exclusion
///
/// # Description
///
/// This structure behaves a lot like a common mutex. There are some differences:
///
/// - By using busy waiting, it can be used outside the runtime.
/// - It is a so called ticket lock and is completly fair.
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
struct Spinlock<T: ?Sized> {
    queue: AtomicUsize,
    dequeue: AtomicUsize,
    data: UnsafeCell<T>,
}

unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
struct SpinlockGuard<'a, T: ?Sized + 'a> {
    dequeue: &'a AtomicUsize,
    data: &'a mut T,
}

impl<T> Spinlock<T> {
    pub const fn new(user_data: T) -> Spinlock<T> {
        Spinlock {
           queue: AtomicUsize::new(0),
           dequeue: AtomicUsize::new(1),
	   data: UnsafeCell::new(user_data),
        }
    }

    #[inline]
    fn obtain_lock(&self) {
        let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
        while self.dequeue.load(Ordering::SeqCst) != ticket {
            spin_loop_hint();
        }
    }

    #[inline]
    pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
        self.obtain_lock();
        SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() }
    }
}

impl<T: ?Sized + Default> Default for Spinlock<T> {
    fn default() -> Spinlock<T> {
        Spinlock::new(Default::default())
    }
}

impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        &*self.data
    }
}

impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut *self.data
    }
}

impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
    /// The dropping of the SpinlockGuard will release the lock it was created from.
    fn drop(&mut self) {
        self.dequeue.fetch_add(1, Ordering::SeqCst);
    }
}

/// Realize a priority queue for tasks
struct PriorityQueue {
    queues: [Option<VecDeque<abi::Tid>>; abi::NO_PRIORITIES],
    prio_bitmap: u64,
}

impl PriorityQueue {
    pub const fn new() -> PriorityQueue {
        PriorityQueue {
            queues: [
                None, None, None, None, None, None, None, None, None, None, None, None, None, None,
                None, None, None, None, None, None, None, None, None, None, None, None, None, None,
                None, None, None,
            ],
            prio_bitmap: 0,
        }
    }

    /// Add a task id by its priority to the queue
    pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) {
        let i: usize = prio.into().into();
        self.prio_bitmap |= (1 << i) as u64;
        if let Some(queue) = &mut self.queues[i] {
            queue.push_back(id);
        } else {
            let mut queue = VecDeque::new();
            queue.push_back(id);
            self.queues[i] = Some(queue);
        }
    }

    fn pop_from_queue(&mut self, queue_index: usize) -> Option<abi::Tid> {
        if let Some(queue) = &mut self.queues[queue_index] {
            let id = queue.pop_front();

            if queue.is_empty() {
                self.prio_bitmap &= !(1 << queue_index as u64);
            }

            id
        } else {
            None
        }
    }

    /// Pop the task handle with the highest priority from the queue
    pub fn pop(&mut self) -> Option<abi::Tid> {
        for i in 0..abi::NO_PRIORITIES {
            if self.prio_bitmap & (1 << i) != 0 {
                return self.pop_from_queue(i);
            }
        }

        None
    }
}

struct MutexInner {
    locked: bool,
    blocked_task: PriorityQueue,
}

impl MutexInner {
    pub const fn new() -> MutexInner {
        MutexInner { locked: false, blocked_task: PriorityQueue::new() }
    }
}

pub struct Mutex {
    inner: Spinlock<MutexInner>,
}

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

impl Mutex {
    pub const fn new() -> Mutex {
        Mutex { inner: Spinlock::new(MutexInner::new()) }
    }

    #[inline]
    pub unsafe fn init(&mut self) {
        self.inner = Spinlock::new(MutexInner::new());
    }

    #[inline]
    pub unsafe fn lock(&self) {
        loop {
            let mut guard = self.inner.lock();
            if guard.locked == false {
                guard.locked = true;
                return;
            } else {
                let prio = abi::get_priority();
                let id = abi::getpid();

                guard.blocked_task.push(prio, id);
                abi::block_current_task();
                drop(guard);
                abi::yield_now();
            }
        }
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        let mut guard = self.inner.lock();
        guard.locked = false;
        if let Some(tid) = guard.blocked_task.pop() {
            abi::wakeup_task(tid);
        }
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        let mut guard = self.inner.lock();
        if guard.locked == false {
            guard.locked = true;
        }
        guard.locked
    }

    #[inline]
    pub unsafe fn destroy(&self) {}
}

pub struct ReentrantMutex {
    inner: *const c_void,
}

impl ReentrantMutex {
    pub const unsafe fn uninitialized() -> ReentrantMutex {
        ReentrantMutex { inner: ptr::null() }
    }

    #[inline]
    pub unsafe fn init(&self) {
        let _ = abi::recmutex_init(&self.inner as *const *const c_void as *mut _);
    }

    #[inline]
    pub unsafe fn lock(&self) {
        let _ = abi::recmutex_lock(self.inner);
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        true
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        let _ = abi::recmutex_unlock(self.inner);
    }

    #[inline]
    pub unsafe fn destroy(&self) {
        let _ = abi::recmutex_destroy(self.inner);
    }
}
<|endoftext|>


<commit_before>use num::{Signed, CheckedAdd, CheckedMul, Saturating, One};
use array_ext::{Array as ArrayExtArray, *};
use std::cmp::{min, max};
use array::{Array, ReplaceItemType};
use arrayvec::{self, ArrayVec};

use super::*;

pub trait PolynomialBase1 {
  type Coefficient: DoubleSizedInteger + Signed;
}
pub trait PolynomialBase2: PolynomialBase1 + Array + arrayvec::Array<Item = <Self as PolynomialBase1>::Coefficient> + ReplaceItemType<[<Self as PolynomialBase1>::Coefficient; 2]> {}

/// Evaluate all Taylor coefficients of a polynomial.
///
/// Returns None if any of the coefficients do not fit in the type.
pub trait AllTaylorCoefficients<Input>: Sized {
  fn all_taylor_coefficients(&self, input: impl Into<Input>)->Option<Self>;
}

macro_rules! impl_polynomials {
  ($($coefficients: expr),*) => {
$(

impl <Coefficient: DoubleSizedInteger + Signed> PolynomialBase1 for [Coefficient; $coefficients] {type Coefficient = Coefficient; }
impl <Coefficient: DoubleSizedInteger + Signed> PolynomialBase2 for [Coefficient; $coefficients] {}

impl <Coefficient: DoubleSizedInteger> AllTaylorCoefficients<<Coefficient as DoubleSizedInteger>::Type> for [Coefficient; $coefficients] {
  fn all_taylor_coefficients(&self, input: impl Into<<Coefficient as DoubleSizedInteger>::Type>)->Option <Self> {
    let input = input.into();
    let mut intermediates: [<Coefficient as DoubleSizedInteger>::Type; $coefficients] = self.map (| coefficient | coefficient.into());
    for first_source in (1..intermediates.len()).rev() {
      for source in first_source..intermediates.len() {
        intermediates[source - 1] = intermediates [source - 1].checked_add (&intermediates[source].checked_mul (&input)?)?;
      }
    }
    let mut output = [Coefficient::zero(); $coefficients];
    for (index, value) in intermediates.iter().enumerate() {
      output [index] = (*value).try_into().ok()?;
    }
    Some (output)
  }

}

)*

  }
}

impl_polynomials!(1,2,3,4,5);

pub trait Polynomial: PolynomialBase1 + PolynomialBase2 + AllTaylorCoefficients<<<Self as PolynomialBase1>::Coefficient as DoubleSizedInteger>::Type> {}
impl<P: PolynomialBase1 + PolynomialBase2 + AllTaylorCoefficients<<<Self as PolynomialBase1>::Coefficient as DoubleSizedInteger>::Type>> Polynomial for P {}

pub struct RangeSearchEndpoint<P: PolynomialBase2> {
  time: P::Coefficient,
  coefficients: P,
}

pub fn coefficient_ranges<P: PolynomialBase2> (endpoints: [& RangeSearchEndpoint<P>; 2])-><P as ReplaceItemType<[P::Coefficient; 2]>>::Type {
  let mut result: <P as ReplaceItemType<[P::Coefficient; 2]>>::Type = array_ext::Array::from_fn(|_| [P::Coefficient::zero(), P::Coefficient::zero()]);
  // TODO what if overflow
  let duration = endpoints [1].time - endpoints [0].time;
  let mut previous = [P::Coefficient::zero(), P::Coefficient::zero()];
  for exponent in (0..result.len()).rev() {
    let left_max = endpoints [0].coefficients.as_slice() [exponent].saturating_add(max(P::Coefficient::zero(), previous[1]).saturating_mul(duration));
    let left_min = endpoints [0].coefficients.as_slice() [exponent].saturating_add(min(P::Coefficient::zero(), previous[0]).saturating_mul(duration));
    let right_max = endpoints [1].coefficients.as_slice() [exponent].saturating_add(max(P::Coefficient::zero(), -previous[0]).saturating_mul(duration));
    let right_min = endpoints [1].coefficients.as_slice() [exponent].saturating_add(min(P::Coefficient::zero(), -previous[1]).saturating_mul(duration));
    let bounds = [max(left_min, right_min), min(left_max, right_max)];
    previous = bounds;
    result.as_mut_slice() [exponent] = bounds;
  }
  result
}

pub struct RangeSearch<P: PolynomialBase2> {
  polynomial: P,
  stack: ArrayVec<[RangeSearchEndpoint<P>; 64]>,
  next_jump: P::Coefficient,
}

impl<P: Polynomial> RangeSearch<P> {
  pub fn new(polynomial: P, start_time: P::Coefficient)->Option<Self> {
    let mut result = RangeSearch {polynomial, stack: ArrayVec::new(), next_jump: P::Coefficient::one()};
    if let Some(coefficients) = result.polynomial.all_taylor_coefficients (start_time) {
      result.stack.push(RangeSearchEndpoint {time: start_time, coefficients});
      result.add_next_endpoint();
      Some(result)
    }
    else {
      None
    }
  }
  pub fn latest_interval(&self)->[&RangeSearchEndpoint<P>; 2] {
    [
      &self.stack[self.stack.len() - 1],
      &self.stack[self.stack.len() - 2],
    ]
  }
  pub fn split_latest(&mut self) {
    let split_time = mean_floor(self.latest_interval()[0].time, self.latest_interval()[1].time);
    if !self.add_endpoint (split_time) {
      let earliest = self.stack.pop().unwrap();
      self.stack.clear();
      self.stack.push(earliest);
      self.add_next_endpoint();
    }
  }
  pub fn skip_latest(&mut self) {
    self.stack.pop() ;
    if self.stack.len() < 2 {
      self.add_next_endpoint();
    }
  }
  pub fn reached_overflow(&self)->bool {
    self.stack.len() == 2 && self.stack [0].time == self.stack [1].time
  }
  fn add_next_endpoint(&mut self) {
    let last_endpoint_time = self.stack.last().unwrap().time;
    loop {
      let endpoint_time = last_endpoint_time.saturating_add (self.next_jump);
      if self.add_endpoint(endpoint_time) { break; }
      self.next_jump = self.next_jump >> 1;
    }
    self.next_jump = self.next_jump << 1;
  }
  fn add_endpoint(&mut self, time: P::Coefficient)->bool {
    if let Some(coefficients) = self.polynomial.all_taylor_coefficients (time) {
      self.stack.insert (self.stack.len() - 1, RangeSearchEndpoint {time, coefficients});
      true
    }
    else {
      false
    }
  }
}

pub fn next_time_lt <P: Polynomial> (polynomial: P, start_time: P::Coefficient, threshold: P::Coefficient)->Option <P::Coefficient> {
  let mut search = RangeSearch::new(polynomial, start_time)?;

  loop {
    if coefficient_ranges(search.latest_interval()).as_slice()[0][0] < threshold {
      if search.latest_interval()[0].time + P::Coefficient::one() == search.latest_interval()[1].time {
        if search.latest_interval()[0].coefficients.as_slice()[0] < threshold {
          return Some(search.latest_interval()[0].time);
        }
        search.skip_latest();
      }
      else {
        search.split_latest();
      }
    }
    else {
      search.skip_latest();
    }
    if search.reached_overflow() { return None; }
  }
}

/*
fn next_time_in_bounds_2d <T: Polynomial, Input> (polynomials: [T;2], start_time: Input, min: [Coefficient;2], max: Coefficient)->Option <Time> {
  let search: ThresholdSearch = ([min, max+1]);
  while a time when the answer is not yet computed comes before the first time when the answer is known to be within bounds {
    (search with the earliest time not yet computed when the answer is also not yet computed).refine_first_bracketed_root(range of times where it matters);
  }
  search.
}*/

#[cfg(test)]
mod tests {

use super::*;
//use proptest::prelude::*;
use num::{BigInt, One};

fn naive_perfect_evaluate <Coefficient: Integer> (coefficients: & [Coefficient], input: Coefficient)->BigInt where BigInt: From<Coefficient> {
  let input = BigInt::from (input) ;
  let mut result = BigInt::zero();
  for (exponent, coefficient) in coefficients.iter().enumerate() {
    let mut term = BigInt::from(*coefficient);
    for _ in 0..exponent { term = term * &input; }
    result = result + term;
  }
  result
}

fn naive_factorial (value: usize)->BigInt {
  let mut result = BigInt::one();
  for factor in 2..=value {
    result = result * BigInt::from(factor);
  }
  result
}

fn naive_binomial_coefficient(n:usize,k:usize)->BigInt {
  naive_factorial (n)/(naive_factorial (k)*naive_factorial (n-k))
}

fn naive_perfect_nth_taylor_coefficient <Coefficient: Integer> (coefficients: & [Coefficient], input: Coefficient, n: usize)->BigInt where BigInt: From<Coefficient> {
  let input = BigInt::from (input) ;
  let mut result = BigInt::zero();
  for (exponent, coefficient) in coefficients.iter().enumerate().skip(n) {
    let mut term = BigInt::from(*coefficient);
    for _ in n..exponent { term = term * &input; }
    result = result + term * naive_binomial_coefficient (exponent, n);
  }
  result
}

macro_rules! test_polynomials {
  ($($coefficients: expr, $integer: ident, $double: ident, $uniform: ident, $name: ident,)*) => {
$(
  mod $name {
    use super::*;
    //use super::super::*;
    use proptest::prelude::*;
    proptest! {
      #[test]
      fn randomly_test_polynomial_translation_inverts (coefficients in prop::array::$uniform(-16 as $integer..16), input in -16 as $integer..16) {
        let translated = coefficients.all_taylor_coefficients (input);
        prop_assume! (translated.is_some());
        let translated = translated.unwrap();
        let translated_back = translated.all_taylor_coefficients (-input);
        prop_assert! (translated_back.is_some(), "we know that the original value was in bounds, so translating back should return some");
        prop_assert_eq! (coefficients, translated_back.unwrap());

      }

      #[test]
      fn randomly_test_taylor_coefficients_evaluates (coefficients in prop::array::$uniform(-16 as $integer..16), input in -16 as $integer..16) {
        let translated = coefficients.all_taylor_coefficients (input);
        prop_assume! (translated.is_some());
        let translated = translated.unwrap();
        let evaluated = naive_perfect_evaluate (&coefficients, input);
        prop_assert_eq! (BigInt::from(translated[0]), evaluated);
        for which in 0..$coefficients {
          prop_assert_eq! (BigInt::from(translated[which]), naive_perfect_nth_taylor_coefficient(&coefficients, input, which), "Incorrect {}th taylor coefficient ", which);
        }
      }

      #[test]
      fn randomly_test_next_time_lt_is_lt (coefficients in prop::array::$uniform(-16 as $integer..16), input in -16 as $integer..16, threshold in -16 as $integer..16) {
        let time = next_time_lt (coefficients, input, threshold);
        prop_assume! (time .is_some());
        let time = time.unwrap();
        if let Some(coefficients) = coefficients.all_taylor_coefficients (time) { prop_assert!(coefficients[0] < threshold); }
      }

      #[test]
      fn randomly_test_next_time_lt_is_next (coefficients in prop::array::$uniform(-16 as $integer..16), input in -16 as $integer..16, threshold in -16 as $integer..16, test_frac in 0f64..1f64) {
        let time = next_time_lt (coefficients, input, threshold);
        let last_not_lt = match time {
          None => $integer::max_value(),
          Some(k) => {
            prop_assert!(k >= input);
            k-1
          },
        };
        prop_assume!(last_not_lt >= input);
        if let Some(first_coefficients) = coefficients.all_taylor_coefficients (input) { prop_assert!(first_coefficients[0] >= threshold); }
        if let Some(last_coefficients) = coefficients.all_taylor_coefficients (last_not_lt) {prop_assert!(last_coefficients[0] >= threshold); }
        let test_time = input + ((last_not_lt.saturating_sub(input)) as f64 * test_frac).floor() as $integer;
        if let Some(test_coefficients) = coefficients.all_taylor_coefficients (test_time) {prop_assert!(test_coefficients[0] >= threshold); }
      }
    }
  }
)*

  }
}

test_polynomials!(
  1, i32, i64, uniform1, polynomial_tests_1,
  2, i32, i64, uniform2, polynomial_tests_2,
  3, i32, i64, uniform3, polynomial_tests_3,
  4, i32, i64, uniform4, polynomial_tests_4,
  5, i32, i64, uniform5, polynomial_tests_5,
);

}
<commit_msg>fixed test failure<commit_after>use num::{Signed, CheckedAdd, CheckedMul, Saturating, One, FromPrimitive};
use array_ext::{Array as ArrayExtArray, *};
use std::cmp::{min, max};
use array::{Array, ReplaceItemType};
use arrayvec::{self, ArrayVec};

use super::*;

pub trait PolynomialBase1 {
  type Coefficient: DoubleSizedInteger + Signed;
}
pub trait PolynomialBase2: PolynomialBase1 + Array + arrayvec::Array<Item = <Self as PolynomialBase1>::Coefficient> + ReplaceItemType<[<Self as PolynomialBase1>::Coefficient; 2]> {}

/// Evaluate all Taylor coefficients of a polynomial.
///
/// Returns None if any of the coefficients do not fit in the type.
pub trait AllTaylorCoefficients<Input>: Sized {
  fn all_taylor_coefficients(&self, input: impl Into<Input>)->Option<Self>;
}

macro_rules! impl_polynomials {
  ($($coefficients: expr),*) => {
$(

impl <Coefficient: DoubleSizedInteger + Signed> PolynomialBase1 for [Coefficient; $coefficients] {type Coefficient = Coefficient; }
impl <Coefficient: DoubleSizedInteger + Signed> PolynomialBase2 for [Coefficient; $coefficients] {}

impl <Coefficient: DoubleSizedInteger> AllTaylorCoefficients<<Coefficient as DoubleSizedInteger>::Type> for [Coefficient; $coefficients] {
  fn all_taylor_coefficients(&self, input: impl Into<<Coefficient as DoubleSizedInteger>::Type>)->Option <Self> {
    let input = input.into();
    let mut intermediates: [<Coefficient as DoubleSizedInteger>::Type; $coefficients] = self.map (| coefficient | coefficient.into());
    for first_source in (1..intermediates.len()).rev() {
      for source in first_source..intermediates.len() {
        intermediates[source - 1] = intermediates [source - 1].checked_add (&intermediates[source].checked_mul (&input)?)?;
      }
    }
    let mut output = [Coefficient::zero(); $coefficients];
    for (index, value) in intermediates.iter().enumerate() {
      output [index] = (*value).try_into().ok()?;
    }
    Some (output)
  }

}

)*

  }
}

impl_polynomials!(1,2,3,4,5);

pub trait Polynomial: PolynomialBase1 + PolynomialBase2 + AllTaylorCoefficients<<<Self as PolynomialBase1>::Coefficient as DoubleSizedInteger>::Type> {}
impl<P: PolynomialBase1 + PolynomialBase2 + AllTaylorCoefficients<<<Self as PolynomialBase1>::Coefficient as DoubleSizedInteger>::Type>> Polynomial for P {}

pub struct RangeSearchEndpoint<P: PolynomialBase2> {
  time: P::Coefficient,
  coefficients: P,
}

pub fn coefficient_ranges<P: PolynomialBase2> (endpoints: [& RangeSearchEndpoint<P>; 2])-><P as ReplaceItemType<[P::Coefficient; 2]>>::Type {
  let mut result: <P as ReplaceItemType<[P::Coefficient; 2]>>::Type = array_ext::Array::from_fn(|_| [P::Coefficient::zero(), P::Coefficient::zero()]);
  // TODO what if overflow
  let duration = endpoints [1].time - endpoints [0].time;
  let mut previous = [P::Coefficient::zero(), P::Coefficient::zero()];
  for exponent in (0..result.len()).rev() {
    let left_max = endpoints [0].coefficients.as_slice() [exponent].saturating_add(max(P::Coefficient::zero(), previous[1]).saturating_mul(duration));
    let left_min = endpoints [0].coefficients.as_slice() [exponent].saturating_add(min(P::Coefficient::zero(), previous[0]).saturating_mul(duration));
    let right_max = endpoints [1].coefficients.as_slice() [exponent].saturating_add(max(P::Coefficient::zero(), -previous[0]).saturating_mul(duration));
    let right_min = endpoints [1].coefficients.as_slice() [exponent].saturating_add(min(P::Coefficient::zero(), -previous[1]).saturating_mul(duration));
    let bounds = [max(left_min, right_min), min(left_max, right_max)];
    previous = bounds.map(|a|a.saturating_mul(P::Coefficient::from_usize(exponent).unwrap()));
    result.as_mut_slice() [exponent] = bounds;
  }
  result
}

pub struct RangeSearch<P: PolynomialBase2> {
  polynomial: P,
  stack: ArrayVec<[RangeSearchEndpoint<P>; 64]>,
  next_jump: P::Coefficient,
}

impl<P: Polynomial> RangeSearch<P> {
  pub fn new(polynomial: P, start_time: P::Coefficient)->Option<Self> {
    let mut result = RangeSearch {polynomial, stack: ArrayVec::new(), next_jump: P::Coefficient::one()};
    if let Some(coefficients) = result.polynomial.all_taylor_coefficients (start_time) {
      result.stack.push(RangeSearchEndpoint {time: start_time, coefficients});
      result.add_next_endpoint();
      Some(result)
    }
    else {
      None
    }
  }
  pub fn latest_interval(&self)->[&RangeSearchEndpoint<P>; 2] {
    [
      &self.stack[self.stack.len() - 1],
      &self.stack[self.stack.len() - 2],
    ]
  }
  pub fn split_latest(&mut self) {
    let split_time = mean_floor(self.latest_interval()[0].time, self.latest_interval()[1].time);
    if !self.add_endpoint (split_time) {
      let earliest = self.stack.pop().unwrap();
      self.stack.clear();
      self.stack.push(earliest);
      self.add_next_endpoint();
    }
  }
  pub fn skip_latest(&mut self) {
    self.stack.pop() ;
    if self.stack.len() < 2 {
      self.add_next_endpoint();
    }
  }
  pub fn reached_overflow(&self)->bool {
    self.stack.len() == 2 && self.stack [0].time == self.stack [1].time
  }
  fn add_next_endpoint(&mut self) {
    let last_endpoint_time = self.stack.last().unwrap().time;
    loop {
      let endpoint_time = last_endpoint_time.saturating_add (self.next_jump);
      if self.add_endpoint(endpoint_time) { break; }
      self.next_jump = self.next_jump >> 1;
    }
    self.next_jump = self.next_jump << 1;
  }
  fn add_endpoint(&mut self, time: P::Coefficient)->bool {
    if let Some(coefficients) = self.polynomial.all_taylor_coefficients (time) {
      self.stack.insert (self.stack.len() - 1, RangeSearchEndpoint {time, coefficients});
      true
    }
    else {
      false
    }
  }
}

pub fn next_time_lt <P: Polynomial> (polynomial: P, start_time: P::Coefficient, threshold: P::Coefficient)->Option <P::Coefficient> {
  let mut search = RangeSearch::new(polynomial, start_time)?;

  loop {
    if coefficient_ranges(search.latest_interval()).as_slice()[0][0] < threshold {
      if search.latest_interval()[0].time + P::Coefficient::one() == search.latest_interval()[1].time {
        if search.latest_interval()[0].coefficients.as_slice()[0] < threshold {
          return Some(search.latest_interval()[0].time);
        }
        search.skip_latest();
      }
      else {
        search.split_latest();
      }
    }
    else {
      search.skip_latest();
    }
    if search.reached_overflow() { return None; }
  }
}

/*
fn next_time_in_bounds_2d <T: Polynomial, Input> (polynomials: [T;2], start_time: Input, min: [Coefficient;2], max: Coefficient)->Option <Time> {
  let search: ThresholdSearch = ([min, max+1]);
  while a time when the answer is not yet computed comes before the first time when the answer is known to be within bounds {
    (search with the earliest time not yet computed when the answer is also not yet computed).refine_first_bracketed_root(range of times where it matters);
  }
  search.
}*/

#[cfg(test)]
mod tests {

use super::*;
//use proptest::prelude::*;
use num::{BigInt, One};

fn naive_perfect_evaluate <Coefficient: Integer> (coefficients: & [Coefficient], input: Coefficient)->BigInt where BigInt: From<Coefficient> {
  let input = BigInt::from (input) ;
  let mut result = BigInt::zero();
  for (exponent, coefficient) in coefficients.iter().enumerate() {
    let mut term = BigInt::from(*coefficient);
    for _ in 0..exponent { term = term * &input; }
    result = result + term;
  }
  result
}

fn naive_factorial (value: usize)->BigInt {
  let mut result = BigInt::one();
  for factor in 2..=value {
    result = result * BigInt::from(factor);
  }
  result
}

fn naive_binomial_coefficient(n:usize,k:usize)->BigInt {
  naive_factorial (n)/(naive_factorial (k)*naive_factorial (n-k))
}

fn naive_perfect_nth_taylor_coefficient <Coefficient: Integer> (coefficients: & [Coefficient], input: Coefficient, n: usize)->BigInt where BigInt: From<Coefficient> {
  let input = BigInt::from (input) ;
  let mut result = BigInt::zero();
  for (exponent, coefficient) in coefficients.iter().enumerate().skip(n) {
    let mut term = BigInt::from(*coefficient);
    for _ in n..exponent { term = term * &input; }
    result = result + term * naive_binomial_coefficient (exponent, n);
  }
  result
}

macro_rules! test_polynomials {
  ($($coefficients: expr, $integer: ident, $double: ident, $uniform: ident, $name: ident,)*) => {
$(
  mod $name {
    use super::*;
    //use super::super::*;
    use proptest::prelude::*;
    proptest! {
      #[test]
      fn randomly_test_polynomial_translation_inverts (coefficients in prop::array::$uniform(-16 as $integer..16), input in -16 as $integer..16) {
        let translated = coefficients.all_taylor_coefficients (input);
        prop_assume! (translated.is_some());
        let translated = translated.unwrap();
        let translated_back = translated.all_taylor_coefficients (-input);
        prop_assert! (translated_back.is_some(), "we know that the original value was in bounds, so translating back should return some");
        prop_assert_eq! (coefficients, translated_back.unwrap());

      }

      #[test]
      fn randomly_test_taylor_coefficients_evaluates (coefficients in prop::array::$uniform(-16 as $integer..16), input in -16 as $integer..16) {
        let translated = coefficients.all_taylor_coefficients (input);
        prop_assume! (translated.is_some());
        let translated = translated.unwrap();
        let evaluated = naive_perfect_evaluate (&coefficients, input);
        prop_assert_eq! (BigInt::from(translated[0]), evaluated);
        for which in 0..$coefficients {
          prop_assert_eq! (BigInt::from(translated[which]), naive_perfect_nth_taylor_coefficient(&coefficients, input, which), "Incorrect {}th taylor coefficient ", which);
        }
      }

      #[test]
      fn randomly_test_next_time_lt_is_lt (coefficients in prop::array::$uniform(-16 as $integer..16), input in -16 as $integer..16, threshold in -16 as $integer..16) {
        let time = next_time_lt (coefficients, input, threshold);
        prop_assume! (time .is_some());
        let time = time.unwrap();
        if let Some(coefficients) = coefficients.all_taylor_coefficients (time) { prop_assert!(coefficients[0] < threshold); }
      }

      #[test]
      fn randomly_test_next_time_lt_is_next (coefficients in prop::array::$uniform(-16 as $integer..16), input in -16 as $integer..16, threshold in -16 as $integer..16, test_frac in 0f64..1f64) {
        let time = next_time_lt (coefficients, input, threshold);
        let last_not_lt = match time {
          None => $integer::max_value(),
          Some(k) => {
            prop_assert!(k >= input);
            k-1
          },
        };
        prop_assume!(last_not_lt >= input);
        if let Some(first_coefficients) = coefficients.all_taylor_coefficients (input) { prop_assert!(first_coefficients[0] >= threshold); }
        if let Some(last_coefficients) = coefficients.all_taylor_coefficients (last_not_lt) {prop_assert!(last_coefficients[0] >= threshold); }
        let test_time = input + ((last_not_lt.saturating_sub(input)) as f64 * test_frac).floor() as $integer;
        if let Some(test_coefficients) = coefficients.all_taylor_coefficients (test_time) {prop_assert!(test_coefficients[0] >= threshold); }
      }
    }
  }
)*

  }
}

test_polynomials!(
  1, i32, i64, uniform1, polynomial_tests_1,
  2, i32, i64, uniform2, polynomial_tests_2,
  3, i32, i64, uniform3, polynomial_tests_3,
  4, i32, i64, uniform4, polynomial_tests_4,
  5, i32, i64, uniform5, polynomial_tests_5,
);

}
<|endoftext|>


<commit_before>use runtime::Error;
use memory::Memory;
use values::{RcValue, Value};

pub static PRIMITIVES: [&'static str; 19] = [
    "*",
    "+",
    "-",
    "/",
    "<",
    "<=",
    "=",
    ">",
    ">=",
    "car",
    "cdr",
    "cons",
    "length",
    "list",
    "list?",
    "not",
    "null?",
    "pair?",
    "display",
];

pub fn apply(name: &String, args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    match name.as_slice() {
        "*"      => product(args, mem),
        "+"      => plus(args, mem),
        "-"      => minus(args, mem),
        "/"      => division(args, mem),
        "<"      => less_than(args, mem),
        "<="     => less_than_or_equal(args, mem),
        "="      => equals(args, mem),
        ">"      => greater_than(args, mem),
        ">="     => greater_than_or_equal(args, mem),
        "car"    => car(args),
        "cdr"    => cdr(args, mem),
        "cons"   => cons(args, mem),
        "length" => length(args, mem),
        "list"   => list(args, mem),
        "list?"  => list_question_mark(args, mem),
        "not"    => not(args, mem),
        "null?"  => null(args, mem),
        "pair?"  => pair(args, mem),
        "display"  => display(args, mem),
        _        => Err(Error::PrimitiveFailed(name.to_string()))
    }
}

fn plus(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    let integers = try!(list_of_integers(args));
    let sum = integers.into_iter().fold(0, |sum, n| sum + n);
    Ok(mem.integer(sum))
}

fn minus(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() == 0 {
        return Err(Error::BadArity(Some("-".to_string())))
    }

    let integers = try!(list_of_integers(args));
    let first = integers[0];

    if integers.len() == 1 {
        Ok(mem.integer(-first))
    } else {
        let sum_of_the_rest = integers.into_iter().skip(1).fold(0, |sum, n| sum + n);
        Ok(mem.integer(first - sum_of_the_rest))
    }
}

fn division(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() == 0 {
        return Err(Error::BadArity(Some("/".to_string())))
    }

    let integers = try!(list_of_integers(args));
    let first = integers[0];

    if integers.len() == 1 {
        Ok(mem.integer(1 / first))
    } else {
        let div = integers.into_iter().skip(1).fold(first, |div, n| div / n);
        Ok(mem.integer(div))
    }
}

fn product(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    let integers = try!(list_of_integers(args));
    let product = integers.into_iter().fold(1, |product, n| product * n);
    Ok(mem.integer(product))
}

fn equals(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() < 2 {
        return Ok(mem.b_true());
    }

    let integers = try!(list_of_integers(args));
    let first = integers[0];

    let equality = integers.into_iter().skip(1).all(|n| n == first);
    Ok(mem.boolean(equality))
}

fn less_than(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    ord(args, mem, |a, b| a < b)
}

fn less_than_or_equal(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    ord(args, mem, |a, b| a <= b)
}

fn greater_than(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    ord(args, mem, |a, b| a > b)
}

fn greater_than_or_equal(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    ord(args, mem, |a, b| a >= b)
}

fn not(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("not".to_string())))
    }

    Ok(mem.boolean(args[0] == mem.b_false()))
}

fn list(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    Ok(mem.list(args))
}

fn length(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("length".to_string())));
    }

    match *args[0] {
        Value::List(ref list) =>
            Ok(mem.integer(list.len() as i64)),
        _ =>
            Err(Error::WrongArgumentType(args[0].clone())),
    }
}

fn pair(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("pair?".to_string())));
    }

    let outcome = match *args[0] {
        Value::List(ref list) =>
            !list.is_empty(),
        Value::DottedList(ref _list, ref _el) =>
            true,
        _ =>
            false,
    };

    Ok(mem.boolean(outcome))
}

fn cons(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 2 {
        return Err(Error::BadArity(Some("cons".to_string())));
    }

    let mut list: Vec<RcValue> = Vec::new();
    list.push(args[0].clone());

    if let Value::List(ref l) = *args[1] {
        for item in l.iter() {
            list.push(item.clone());
        }

        Ok(mem.list(list))
    } else {
        Ok(mem.dotted_list(list, args[1].clone()))
    }
}

fn car(args: Vec<RcValue>) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("car".to_string())));
    }

    match *args[0] {
        Value::List(ref l) if !l.is_empty() =>
            Ok(l[0].clone()),
        Value::DottedList(ref l, ref _t) =>
            Ok(l[0].clone()),
        _ =>
            Err(Error::WrongArgumentType(args[0].clone())),
    }
}

fn cdr(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("cdr".to_string())));
    }

    match *args[0] {
        Value::List(ref l) if !l.is_empty() =>
            match l.tail() {
                tail if tail.is_empty() =>
                    Ok(mem.empty_list()),
                tail => {
                    let list = tail.iter().map(|v| v.clone()).collect();
                    Ok(mem.list(list))
                }
            },
        Value::DottedList(ref _l, ref t) =>
            Ok(t.clone()),
        _ =>
            Err(Error::WrongArgumentType(args[0].clone())),
    }
}

fn null(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("null?".to_string())));
    }

    Ok(mem.boolean(args[0] == mem.empty_list()))
}

fn list_question_mark(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("list?".to_string())));
    }

    let outcome = if let Value::List(ref _l) = *args[0] {
        true
    } else {
        false
    };

    Ok(mem.boolean(outcome))
}

fn list_of_integers(list: Vec<RcValue>) -> Result<Vec<i64>, Error> {
    let mut integers = Vec::with_capacity(list.len());

    for val in list.into_iter() {
        match *val {
            Value::Integer(n) =>
                integers.push(n),
            _ =>
                return Err(Error::WrongArgumentType(val.clone())),
        }
    }

    Ok(integers)
}

fn ord(args: Vec<RcValue>, mem: &mut Memory, cmp: |i64, i64| -> bool) -> Result<RcValue, Error> {
    if args.len() < 2 {
        return Ok(mem.b_true())
    }

    let integers = try!(list_of_integers(args));
    let outcome = range(0u, integers.len() - 1).all(|i|
        cmp(integers[i], integers[i + 1])
    );

    return Ok(mem.boolean(outcome))
}

fn display(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("display".to_string())));
    }

    print!("{}", args.first().unwrap());

    Ok(mem.b_true())
}
<commit_msg>Added primitive `newline`.<commit_after>use runtime::Error;
use memory::Memory;
use values::{RcValue, Value};

pub static PRIMITIVES: [&'static str; 20] = [
    "*",
    "+",
    "-",
    "/",
    "<",
    "<=",
    "=",
    ">",
    ">=",
    "car",
    "cdr",
    "cons",
    "length",
    "list",
    "list?",
    "not",
    "null?",
    "pair?",
    "display",
    "newline",
];

pub fn apply(name: &String, args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    match name.as_slice() {
        "*"      => product(args, mem),
        "+"      => plus(args, mem),
        "-"      => minus(args, mem),
        "/"      => division(args, mem),
        "<"      => less_than(args, mem),
        "<="     => less_than_or_equal(args, mem),
        "="      => equals(args, mem),
        ">"      => greater_than(args, mem),
        ">="     => greater_than_or_equal(args, mem),
        "car"    => car(args),
        "cdr"    => cdr(args, mem),
        "cons"   => cons(args, mem),
        "length" => length(args, mem),
        "list"   => list(args, mem),
        "list?"  => list_question_mark(args, mem),
        "not"    => not(args, mem),
        "null?"  => null(args, mem),
        "pair?"  => pair(args, mem),
        "display"  => display(args, mem),
        "newline"  => newline(args, mem),
        _        => Err(Error::PrimitiveFailed(name.to_string()))
    }
}

fn plus(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    let integers = try!(list_of_integers(args));
    let sum = integers.into_iter().fold(0, |sum, n| sum + n);
    Ok(mem.integer(sum))
}

fn minus(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() == 0 {
        return Err(Error::BadArity(Some("-".to_string())))
    }

    let integers = try!(list_of_integers(args));
    let first = integers[0];

    if integers.len() == 1 {
        Ok(mem.integer(-first))
    } else {
        let sum_of_the_rest = integers.into_iter().skip(1).fold(0, |sum, n| sum + n);
        Ok(mem.integer(first - sum_of_the_rest))
    }
}

fn division(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() == 0 {
        return Err(Error::BadArity(Some("/".to_string())))
    }

    let integers = try!(list_of_integers(args));
    let first = integers[0];

    if integers.len() == 1 {
        Ok(mem.integer(1 / first))
    } else {
        let div = integers.into_iter().skip(1).fold(first, |div, n| div / n);
        Ok(mem.integer(div))
    }
}

fn product(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    let integers = try!(list_of_integers(args));
    let product = integers.into_iter().fold(1, |product, n| product * n);
    Ok(mem.integer(product))
}

fn equals(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() < 2 {
        return Ok(mem.b_true());
    }

    let integers = try!(list_of_integers(args));
    let first = integers[0];

    let equality = integers.into_iter().skip(1).all(|n| n == first);
    Ok(mem.boolean(equality))
}

fn less_than(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    ord(args, mem, |a, b| a < b)
}

fn less_than_or_equal(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    ord(args, mem, |a, b| a <= b)
}

fn greater_than(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    ord(args, mem, |a, b| a > b)
}

fn greater_than_or_equal(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    ord(args, mem, |a, b| a >= b)
}

fn not(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("not".to_string())))
    }

    Ok(mem.boolean(args[0] == mem.b_false()))
}

fn list(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    Ok(mem.list(args))
}

fn length(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("length".to_string())));
    }

    match *args[0] {
        Value::List(ref list) =>
            Ok(mem.integer(list.len() as i64)),
        _ =>
            Err(Error::WrongArgumentType(args[0].clone())),
    }
}

fn pair(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("pair?".to_string())));
    }

    let outcome = match *args[0] {
        Value::List(ref list) =>
            !list.is_empty(),
        Value::DottedList(ref _list, ref _el) =>
            true,
        _ =>
            false,
    };

    Ok(mem.boolean(outcome))
}

fn cons(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 2 {
        return Err(Error::BadArity(Some("cons".to_string())));
    }

    let mut list: Vec<RcValue> = Vec::new();
    list.push(args[0].clone());

    if let Value::List(ref l) = *args[1] {
        for item in l.iter() {
            list.push(item.clone());
        }

        Ok(mem.list(list))
    } else {
        Ok(mem.dotted_list(list, args[1].clone()))
    }
}

fn car(args: Vec<RcValue>) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("car".to_string())));
    }

    match *args[0] {
        Value::List(ref l) if !l.is_empty() =>
            Ok(l[0].clone()),
        Value::DottedList(ref l, ref _t) =>
            Ok(l[0].clone()),
        _ =>
            Err(Error::WrongArgumentType(args[0].clone())),
    }
}

fn cdr(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("cdr".to_string())));
    }

    match *args[0] {
        Value::List(ref l) if !l.is_empty() =>
            match l.tail() {
                tail if tail.is_empty() =>
                    Ok(mem.empty_list()),
                tail => {
                    let list = tail.iter().map(|v| v.clone()).collect();
                    Ok(mem.list(list))
                }
            },
        Value::DottedList(ref _l, ref t) =>
            Ok(t.clone()),
        _ =>
            Err(Error::WrongArgumentType(args[0].clone())),
    }
}

fn null(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("null?".to_string())));
    }

    Ok(mem.boolean(args[0] == mem.empty_list()))
}

fn list_question_mark(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("list?".to_string())));
    }

    let outcome = if let Value::List(ref _l) = *args[0] {
        true
    } else {
        false
    };

    Ok(mem.boolean(outcome))
}

fn list_of_integers(list: Vec<RcValue>) -> Result<Vec<i64>, Error> {
    let mut integers = Vec::with_capacity(list.len());

    for val in list.into_iter() {
        match *val {
            Value::Integer(n) =>
                integers.push(n),
            _ =>
                return Err(Error::WrongArgumentType(val.clone())),
        }
    }

    Ok(integers)
}

fn ord(args: Vec<RcValue>, mem: &mut Memory, cmp: |i64, i64| -> bool) -> Result<RcValue, Error> {
    if args.len() < 2 {
        return Ok(mem.b_true())
    }

    let integers = try!(list_of_integers(args));
    let outcome = range(0u, integers.len() - 1).all(|i|
        cmp(integers[i], integers[i + 1])
    );

    return Ok(mem.boolean(outcome))
}

fn display(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity(Some("display".to_string())));
    }

    print!("{}", args.first().unwrap());

    Ok(mem.b_true())
}

fn newline(args: Vec<RcValue>, mem: &mut Memory) -> Result<RcValue, Error> {
    if args.len() != 0 {
        return Err(Error::BadArity(Some("newline".to_string())));
    }

    println!("");

    Ok(mem.b_true())
}
<|endoftext|>


<commit_before>#![feature(unboxed_closures)]
#![feature(plugin)]
#![feature(box_syntax)]
#![feature(std_misc, collections, path_ext)]
#[macro_use]
#[macro_use]
extern crate wtftw;

use std::fs::PathExt;
use std::io::Write;
use std::path::Path;
use std::env;
use std::ffi::AsOsStr;
use wtftw::window_system::*;
use wtftw::window_manager::*;
use wtftw::handlers::default::*;
use wtftw::config::*;
use wtftw::util::*;
use wtftw::layout::*;
use wtftw::layout::Direction::*;
use wtftw::layout::LayoutMessage::*;

#[no_mangle]
pub extern fn configure(_: &mut WindowManager, w: &WindowSystem, config: &mut Config) {
    let modm = MOD1MASK;

    config.general.mod_mask = modm;
    config.general.border_color = 0x404040;
    config.general.focus_border_color = 0xebebeb;
    config.general.border_width = 2;
    config.general.terminal = (String::from_str("termite"), String::from_str(""));
    config.general.layout = LayoutCollection::new(vec!(
            GapLayout::new(8, AvoidStrutsLayout::new(vec!(Direction::Up, Direction::Down), BinarySpacePartition::new())),
            GapLayout::new(8, AvoidStrutsLayout::new(vec!(Direction::Up, Direction::Down), MirrorLayout::new(BinarySpacePartition::new()))),
            NoBordersLayout::new(box FullLayout)));

    config.general.tags = (vec!("一: ターミナル", "二: ウェブ", "三: コード",
                                "四: メディア", "五: スチーム", "六: ラテック",
                                "七: 音楽", "八: im", "九: 残り"))
        .into_iter().map(String::from_str).collect();

    // Register key handlers

    // Some standard key handlers for starting, restarting, etc.
    add_key_handler_str!(config, w, "q",      modm | SHIFTMASK, exit);
    add_key_handler_str!(config, w, "q",      modm,             restart);
    add_key_handler_str!(config, w, "Return", modm | SHIFTMASK, start_terminal);
    add_key_handler_str!(config, w, "p",      modm,             start_launcher);

    // Focus and window movement
    add_key_handler_str!(config, w, "j", modm,             |m, w, c| m.windows(w, c, |x| x.focus_down()));
    add_key_handler_str!(config, w, "k", modm,             |m, w, c| m.windows(w, c, |x| x.focus_up()));
    add_key_handler_str!(config, w, "j", modm | SHIFTMASK, |m, w, c| m.windows(w, c, |x| x.swap_down()));
    add_key_handler_str!(config, w, "k", modm | SHIFTMASK, |m, w, c| m.windows(w, c, |x| x.swap_up()));
    add_key_handler_str!(config, w, "Return", modm,        |m, w, c| m.windows(w, c, |x| x.swap_master()));
    add_key_handler_str!(config, w, "c", modm, |m, w, c| m.kill_window(w).windows(w, c, |x| x.clone()));

    add_key_handler_str!(config, w, "t", modm, |m, w, c| {
        match m.workspaces.peek() {
            Some(window) => m.windows(w, c, |x| x.sink(window)),
            None => m.clone()
        }
    });

    // Layout messages
    add_key_handler_str!(config, w, "h",      modm,             send_layout_message!(LayoutMessage::Decrease));
    add_key_handler_str!(config, w, "l",      modm,             send_layout_message!(LayoutMessage::Increase));
    add_key_handler_str!(config, w, "z",      modm,             send_layout_message!(LayoutMessage::DecreaseSlave));
    add_key_handler_str!(config, w, "a",      modm,             send_layout_message!(LayoutMessage::IncreaseSlave));
    add_key_handler_str!(config, w, "x",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::IncreaseGap));
    add_key_handler_str!(config, w, "s",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::DecreaseGap));
    add_key_handler_str!(config, w, "comma",  modm,             send_layout_message!(LayoutMessage::IncreaseMaster));
    add_key_handler_str!(config, w, "period", modm,             send_layout_message!(LayoutMessage::DecreaseMaster));
    add_key_handler_str!(config, w, "space",  modm,             send_layout_message!(LayoutMessage::Next));
    add_key_handler_str!(config, w, "space",  modm | SHIFTMASK, send_layout_message!(LayoutMessage::Prev));
    add_key_handler_str!(config, w, "r",      modm,             send_layout_message!(LayoutMessage::TreeRotate));
    add_key_handler_str!(config, w, "s",      modm,             send_layout_message!(LayoutMessage::TreeSwap));
    add_key_handler_str!(config, w, "u",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::TreeExpandTowards(Direction::Left)));
    add_key_handler_str!(config, w, "p",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::TreeExpandTowards(Direction::Right)));
    add_key_handler_str!(config, w, "i",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::TreeExpandTowards(Direction::Down)));
    add_key_handler_str!(config, w, "o",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::TreeExpandTowards(Direction::Up)));
    add_key_handler_str!(config, w, "u",      modm | CONTROLMASK, send_layout_message!(LayoutMessage::TreeShrinkFrom(Direction::Left)));
    add_key_handler_str!(config, w, "p",      modm | CONTROLMASK, send_layout_message!(LayoutMessage::TreeShrinkFrom(Direction::Right)));
    add_key_handler_str!(config, w, "i",      modm | CONTROLMASK, send_layout_message!(LayoutMessage::TreeShrinkFrom(Direction::Down)));
    add_key_handler_str!(config, w, "o",      modm | CONTROLMASK, send_layout_message!(LayoutMessage::TreeShrinkFrom(Direction::Up)));

    // Workspace switching and moving
    for i in (1usize..10) {
        add_key_handler_str!(config, w, &i.to_string(), modm,
        move |m, w, c| switch_to_workspace(m, w, c, i - 1));

        add_key_handler_str!(config, w, &i.to_string(), modm | SHIFTMASK,
        move |m, w, c| move_window_to_workspace(m, w, c, i - 1));
    }

    // Media keys
    add_key_handler_str!(config, w, "j", modm | CONTROLMASK, run!("amixer", "-q set Master 5%-"));
    add_key_handler_str!(config, w, "k", modm | CONTROLMASK, run!("amixer", "-q set Master 5%+"));

    add_key_handler_code!(config, 0x1008ff11, NONEMASK, run!("amixer", "-q set Master 5%-"));
    add_key_handler_code!(config, 0x1008ff13, NONEMASK, run!("amixer", "-q set Master 5%+"));

    add_key_handler_code!(config, 0x1008ff02, NONEMASK, run!("xbacklight", "+10"));
    add_key_handler_code!(config, 0x1008ff03, NONEMASK, run!("xbacklight", "-10"));

    add_mouse_handler!(config, BUTTON1, modm,
                       |m, w, c, s| {
                           m.focus(s, w, c).mouse_move_window(w, c, s).windows(w, c, |x| x.shift_master())
                       });
    add_mouse_handler!(config, BUTTON3, modm,
                       |m, w, c, s| {
                           m.focus(s, w, c).mouse_resize_window(w, c, s).windows(w, c, |x| x.shift_master())
                       });

    // Place specific applications on specific workspaces
    config.set_manage_hook(box |workspaces, window_system, window| {
        match &window_system.get_class_name(window)[..] {
            "MPlayer" => spawn_on(workspaces, window_system, window, 3),
            "vlc"     => spawn_on(workspaces, window_system, window, 3),
            _         => workspaces.clone()
        }
    });

    // Xmobar handling and formatting
    let home_dir = env::home_dir().unwrap();
    let home = home_dir.as_os_str().to_str().unwrap();
    let xmobar_config = format!("{}/.xmonad/xmobar1.hs", home);

    if Path::new(&xmobar_config.clone()).is_file() {
        let mut xmobar = spawn_pipe(config, "xmobar",
                                    vec!(xmobar_config));
        let tags = config.general.tags.clone();
        config.set_log_hook(box move |m, w| {
            let p = &mut xmobar;
            let tags = &tags;
            let workspaces = tags.clone().iter()
                .enumerate()
                .map(|(i, _)| if i as u32 == m.workspaces.current.workspace.id {
                    format!("<fc=#d7af87>■</fc>")
                } else if m.workspaces.visible.iter().any(|w| w.workspace.id == i as u32) {
                    format!("<fc=#808080>■</fc>")
                } else {
                    format!("■")
                })
            .fold(String::from_str(""), |a, x| {
                let mut r = a.clone();
                r.push_str(&x);
                r
            });

            let name = match m.workspaces.peek() {
                None => String::from_str(""),
                Some(window) => w.get_window_name(window)
            };
            let content = format!("{} {} {}\n", workspaces, m.workspaces.current.workspace.layout.description(), name);
            match p.write().unwrap().stdin.as_mut() {
                Some(pin) => {
                    match pin.write(content.as_bytes()) {
                        _      => ()
                    };
                    pin.flush().unwrap();
                },
                _ => ()
            }
        });
    };
}
<commit_msg>updated config<commit_after>#![feature(unboxed_closures)]
#![feature(plugin)]
#![feature(box_syntax)]
#![feature(std_misc, collections, path_ext)]
#[macro_use]
#[macro_use]
extern crate wtftw;
extern crate wtftw_contrib;

use std::fs::PathExt;
use std::io::Write;
use std::path::Path;
use std::env;
use std::ffi::AsOsStr;
use wtftw::window_system::*;
use wtftw::window_manager::*;
use wtftw::handlers::default::*;
use wtftw::config::*;
use wtftw::util::*;
use wtftw::layout::Direction;
use wtftw::layout::LayoutMessage;
use wtftw_contrib::layout::{ AvoidStrutsLayout, LayoutCollection, BinarySpacePartition, GapLayout, MirrorLayout, NoBordersLayout, FullLayout };

#[no_mangle]
pub extern fn configure(_: &mut WindowManager, w: &WindowSystem, config: &mut Config) {
    let modm = MOD1MASK;

    config.general.mod_mask = modm;
    config.general.border_color = 0x404040;
    config.general.focus_border_color = 0xebebeb;
    config.general.border_width = 1;
    config.general.terminal = (String::from_str("termite"), String::from_str(""));
    config.general.layout = LayoutCollection::new(vec!(
            GapLayout::new(0, AvoidStrutsLayout::new(vec!(Direction::Up, Direction::Down), BinarySpacePartition::new())),
            GapLayout::new(0, AvoidStrutsLayout::new(vec!(Direction::Up, Direction::Down), MirrorLayout::new(BinarySpacePartition::new()))),
            NoBordersLayout::new(box FullLayout)));

    config.general.tags = (vec!("一: ターミナル", "二: ウェブ", "三: コード",
                                "四: メディア", "五: スチーム", "六: ラテック",
                                "七: 音楽", "八: im", "九: 残り"))
        .into_iter().map(String::from_str).collect();

    // Register key handlers

    // Some standard key handlers for starting, restarting, etc.
    add_key_handler_str!(config, w, "q",      modm | SHIFTMASK, exit);
    add_key_handler_str!(config, w, "q",      modm,             restart);
    add_key_handler_str!(config, w, "Return", modm | SHIFTMASK, start_terminal);
    add_key_handler_str!(config, w, "p",      modm,             start_launcher);

    // Focus and window movement
    add_key_handler_str!(config, w, "j", modm,             |m, w, c| m.windows(w, c, |x| x.focus_down()));
    add_key_handler_str!(config, w, "k", modm,             |m, w, c| m.windows(w, c, |x| x.focus_up()));
    add_key_handler_str!(config, w, "j", modm | SHIFTMASK, |m, w, c| m.windows(w, c, |x| x.swap_down()));
    add_key_handler_str!(config, w, "k", modm | SHIFTMASK, |m, w, c| m.windows(w, c, |x| x.swap_up()));
    add_key_handler_str!(config, w, "Return", modm,        |m, w, c| m.windows(w, c, |x| x.swap_master()));
    add_key_handler_str!(config, w, "c", modm, |m, w, c| m.kill_window(w).windows(w, c, |x| x.clone()));

    add_key_handler_str!(config, w, "t", modm, |m, w, c| {
        match m.workspaces.peek() {
            Some(window) => m.windows(w, c, |x| x.sink(window)),
            None => m.clone()
        }
    });

    // Layout messages
    add_key_handler_str!(config, w, "h",      modm,             send_layout_message!(LayoutMessage::Decrease));
    add_key_handler_str!(config, w, "l",      modm,             send_layout_message!(LayoutMessage::Increase));
    add_key_handler_str!(config, w, "z",      modm,             send_layout_message!(LayoutMessage::DecreaseSlave));
    add_key_handler_str!(config, w, "a",      modm,             send_layout_message!(LayoutMessage::IncreaseSlave));
    add_key_handler_str!(config, w, "x",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::IncreaseGap));
    add_key_handler_str!(config, w, "s",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::DecreaseGap));
    add_key_handler_str!(config, w, "comma",  modm,             send_layout_message!(LayoutMessage::IncreaseMaster));
    add_key_handler_str!(config, w, "period", modm,             send_layout_message!(LayoutMessage::DecreaseMaster));
    add_key_handler_str!(config, w, "space",  modm,             send_layout_message!(LayoutMessage::Next));
    add_key_handler_str!(config, w, "space",  modm | SHIFTMASK, send_layout_message!(LayoutMessage::Prev));
    add_key_handler_str!(config, w, "r",      modm,             send_layout_message!(LayoutMessage::TreeRotate));
    add_key_handler_str!(config, w, "s",      modm,             send_layout_message!(LayoutMessage::TreeSwap));
    add_key_handler_str!(config, w, "u",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::TreeExpandTowards(Direction::Left)));
    add_key_handler_str!(config, w, "p",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::TreeExpandTowards(Direction::Right)));
    add_key_handler_str!(config, w, "i",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::TreeExpandTowards(Direction::Down)));
    add_key_handler_str!(config, w, "o",      modm | SHIFTMASK, send_layout_message!(LayoutMessage::TreeExpandTowards(Direction::Up)));
    add_key_handler_str!(config, w, "u",      modm | CONTROLMASK, send_layout_message!(LayoutMessage::TreeShrinkFrom(Direction::Left)));
    add_key_handler_str!(config, w, "p",      modm | CONTROLMASK, send_layout_message!(LayoutMessage::TreeShrinkFrom(Direction::Right)));
    add_key_handler_str!(config, w, "i",      modm | CONTROLMASK, send_layout_message!(LayoutMessage::TreeShrinkFrom(Direction::Down)));
    add_key_handler_str!(config, w, "o",      modm | CONTROLMASK, send_layout_message!(LayoutMessage::TreeShrinkFrom(Direction::Up)));

    // Workspace switching and moving
    for i in (1usize..10) {
        add_key_handler_str!(config, w, &i.to_string(), modm,
        move |m, w, c| switch_to_workspace(m, w, c, i - 1));

        add_key_handler_str!(config, w, &i.to_string(), modm | SHIFTMASK,
        move |m, w, c| move_window_to_workspace(m, w, c, i - 1));
    }

    // Media keys
    add_key_handler_str!(config, w, "j", modm | CONTROLMASK, run!("amixer", "-q set Master 5%-"));
    add_key_handler_str!(config, w, "k", modm | CONTROLMASK, run!("amixer", "-q set Master 5%+"));

    add_key_handler_code!(config, 0x1008ff11, NONEMASK, run!("amixer", "-q set Master 5%-"));
    add_key_handler_code!(config, 0x1008ff13, NONEMASK, run!("amixer", "-q set Master 5%+"));

    add_key_handler_code!(config, 0x1008ff02, NONEMASK, run!("xbacklight", "+10"));
    add_key_handler_code!(config, 0x1008ff03, NONEMASK, run!("xbacklight", "-10"));

    add_mouse_handler!(config, BUTTON1, modm,
                       |m, w, c, s| {
                           m.focus(s, w, c).mouse_move_window(w, c, s).windows(w, c, |x| x.shift_master())
                       });
    add_mouse_handler!(config, BUTTON3, modm,
                       |m, w, c, s| {
                           m.focus(s, w, c).mouse_resize_window(w, c, s).windows(w, c, |x| x.shift_master())
                       });

    // Place specific applications on specific workspaces
    config.set_manage_hook(box |workspaces, window_system, window| {
        match &window_system.get_class_name(window)[..] {
            "MPlayer" => spawn_on(workspaces, window_system, window, 3),
            "vlc"     => spawn_on(workspaces, window_system, window, 3),
            _         => workspaces.clone()
        }
    });

    // Xmobar handling and formatting
    let home_dir = env::home_dir().unwrap();
    let home = home_dir.as_os_str().to_str().unwrap();
    let xmobar_config = format!("{}/.xmonad/xmobar1.hs", home);

    if Path::new(&xmobar_config.clone()).is_file() {
        let mut xmobar = spawn_pipe(config, "xmobar",
                                    vec!(xmobar_config));
        let tags = config.general.tags.clone();
        config.set_log_hook(box move |m, w| {
            let p = &mut xmobar;
            let tags = &tags;
            let workspaces = tags.clone().iter()
                .enumerate()
                .map(|(i, _)| if i as u32 == m.workspaces.current.workspace.id {
                    format!("<fc=#fb4934>■</fc>")
                } else if m.workspaces.visible.iter().any(|w| w.workspace.id == i as u32) {
                    format!("<fc=#fabd2f>■</fc>")
                } else {
                    format!("■")
                })
            .fold(String::from_str(""), |a, x| {
                let mut r = a.clone();
                r.push_str(&x);
                r
            });

            let name = match m.workspaces.peek() {
                None => String::from_str(""),
                Some(window) => w.get_window_name(window)
            };
            let content = format!("{} {} {}\n", workspaces, m.workspaces.current.workspace.layout.description(), name);
            match p.write().unwrap().stdin.as_mut() {
                Some(pin) => {
                    match pin.write(content.as_bytes()) {
                        _      => ()
                    };
                    pin.flush().unwrap();
                },
                _ => ()
            }
        });
    };
}
<|endoftext|>