<commit_before>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate rand;

pub mod basetypes;
pub mod chess_move;
pub mod position;
pub mod search;
pub mod uci;
pub mod tt;
pub mod time_manager;

use std::process::exit;
use std::sync::Arc;
use std::time::SystemTime;
use std::collections::VecDeque;
use basetypes::NodeCount;
use tt::{Tt, BOUND_EXACT, BOUND_UPPER, BOUND_LOWER};
use position::Position;
use uci::{UciEngine, UciEngineFactory};
use search::{Variation, SearchStatus, SearchThread};
use time_manager::TimeManager;

/// The version of the program.
pub const VERSION: &'static str = "0.1";

// The name of the program.
const NAME: &'static str = "Alcibiades";

// The author of the program.
const AUTHOR: &'static str = "Evgeni Pandurski";

// The starting position in Forsyth–Edwards notation (FEN).
const STARTING_POSITION: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

// Represents a condition for terminating the search.
enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(NodeCount), // Stop when the given number of nodes has been searched.
    Depth(u8), // Stop when the given search depth has been reached.
    Never, // An infinite search.
}

/// Implements `UciEngine` trait.
pub struct Engine {
    tt: Arc<Tt>,
    position: Position,
    current_depth: u8,

    // `Engine` will hand over the real work to `SearchThread`.
    search_thread: SearchThread,

    // Tells the engine when it must stop thinking and play the best
    // move it has found.
    play_when: PlayWhen,

    // Tells the engine how many best lines to calculate and send to
    // the GUI (the first move in each best line is different). This
    // is the so called "MultiPV" mode.
    variation_count: usize,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,

    // `true` if the engine is thinking in pondering mode.
    is_pondering: bool,

    // Marks the last time when a message was sent by the engine to
    // the GUI, or if no message has been sent yet -- the time when
    // the search stared.
    silent_since: SystemTime,

    // A queue for the messages send by the engine to the GUI.
    queue: VecDeque<uci::EngineReply>,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = Tt::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);

        Engine {
            tt: tt.clone(),
            position: Position::from_fen(STARTING_POSITION).ok().unwrap(),
            current_depth: 0,
            search_thread: SearchThread::new(tt),
            play_when: PlayWhen::Never,
            variation_count: 1,
            pondering_is_allowed: false,
            is_pondering: false,
            silent_since: SystemTime::now(),
            queue: VecDeque::new(),
        }
    }

    // A helper method. It it adds a progress report message to
    // `self.queue`.
    fn queue_progress_report(&mut self) {
        let &SearchStatus { depth, searched_nodes, nps, .. } = self.search_thread.status();
        self.queue.push_back(uci::EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
        ]));
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current
    // (multi)PV to `self.queue`.
    fn queue_pv(&mut self) {
        let &SearchStatus { depth, ref variations, searched_nodes, duration_millis, nps, .. } =
            self.search_thread.status();
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut moves_string = String::new();
            for m in moves {
                moves_string.push_str(&m.notation());
                moves_string.push(' ');
            }
            self.queue.push_back(uci::EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("multipv".to_string(), format!("{}", i + 1)),
            ("score".to_string(), format!("cp {}{}", value, bound_suffix)),
            ("time".to_string(), format!("{}", duration_millis)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), moves_string),
        ]));
        }
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current
    // best move to `self.queue`.
    fn queue_best_move(&mut self) {
        let &SearchStatus { ref variations, .. } = self.search_thread.status();
        self.queue.push_back(uci::EngineReply::BestMove {
            best_move: variations[0].moves.get(0).map_or("0000".to_string(), |m| m.notation()),
            ponder_move: variations[0].moves.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            "MultiPV" => {
                self.variation_count = match value.parse::<usize>().unwrap_or(0) {
                    0 => 1,
                    n if n > 500 => 500,
                    n => n,
                };
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        self.tt.clear();
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if let Ok(p) = Position::from_history(fen, moves) {
            self.position = p;
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        // Note: We ignore the "mate" parameter.

        self.search_thread.stop();
        self.tt.new_search();
        self.current_depth = 0;
        self.is_pondering = ponder;
        self.play_when = if infinite {
            PlayWhen::Never
        } else if movetime.is_some() {
            PlayWhen::MoveTime(movetime.unwrap())
        } else if nodes.is_some() {
            PlayWhen::Nodes(nodes.unwrap())
        } else if depth.is_some() {
            PlayWhen::Depth(depth.unwrap() as u8)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(&self.position,
                                                      self.pondering_is_allowed,
                                                      wtime,
                                                      btime,
                                                      winc,
                                                      binc,
                                                      movestogo))
        };
        self.silent_since = SystemTime::now();
        self.search_thread.search(&self.position, self.variation_count, searchmoves);
    }

    fn ponder_hit(&mut self) {
        if self.search_thread.status().done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.search_thread.stop();
        self.queue_best_move();
    }

    fn get_reply(&mut self) -> Option<uci::EngineReply> {
        if !self.search_thread.status().done {
            // Update the search status.
            self.search_thread.update_status();
            let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } =
                self.search_thread.status();

            // Send the (multi)PV when changed.
            if depth > self.current_depth {
                self.current_depth = depth;
                self.queue_pv();
            }

            // Send periodic progress reports.
            if self.silent_since.elapsed().unwrap().as_secs() > 10 {
                self.queue_progress_report();
            }

            // Register the updated search status with the time manager.
            if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
                tm.update_status(self.search_thread.status());
            }

            // Check if we must play now.
            if !self.is_pondering &&
               match self.play_when {
                PlayWhen::TimeManagement(ref tm) => done || tm.must_play(),
                PlayWhen::MoveTime(t) => done || duration_millis >= t,
                PlayWhen::Nodes(n) => done || searched_nodes >= n,
                PlayWhen::Depth(d) => done || depth >= d,
                PlayWhen::Never => false,
            } {
                self.stop();
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.search_thread.join();
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author(&self) -> String {
        AUTHOR.to_string()
    }

    fn options(&self) -> Vec<(uci::OptionName, uci::OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), uci::OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), uci::OptionDescription::Check { default: false }),
            ("MultiPV".to_string(), uci::OptionDescription::Spin { min: 1, max: 500, default: 1 }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

fn main() {
    if let Ok(mut uci_loop) = uci::Server::wait_for_hanshake(EngineFactory) {
        match uci_loop.serve() {
            Ok(_) => {
                exit(0);
            }
            Err(_) => {
                exit(1);
            }
        }
    }
    exit(2);
}
<commit_msg>fixed comment<commit_after>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate rand;

pub mod basetypes;
pub mod chess_move;
pub mod position;
pub mod search;
pub mod uci;
pub mod tt;
pub mod time_manager;

use std::process::exit;
use std::sync::Arc;
use std::time::SystemTime;
use std::collections::VecDeque;
use basetypes::NodeCount;
use tt::{Tt, BOUND_EXACT, BOUND_UPPER, BOUND_LOWER};
use position::Position;
use uci::{UciEngine, UciEngineFactory};
use search::{Variation, SearchStatus, SearchThread};
use time_manager::TimeManager;

/// The version of the program.
pub const VERSION: &'static str = "0.1";

// The name of the program.
const NAME: &'static str = "Alcibiades";

// The author of the program.
const AUTHOR: &'static str = "Evgeni Pandurski";

// The starting position in Forsyth–Edwards notation (FEN).
const STARTING_POSITION: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

// Represents a condition for terminating the search.
enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(NodeCount), // Stop when the given number of nodes has been searched.
    Depth(u8), // Stop when the given search depth has been reached.
    Never, // An infinite search.
}

/// Implements `UciEngine` trait.
pub struct Engine {
    tt: Arc<Tt>,
    position: Position,
    current_depth: u8,

    // `Engine` will hand over the real work to `SearchThread`.
    search_thread: SearchThread,

    // Tells the engine when it must stop thinking and play the best
    // move it has found.
    play_when: PlayWhen,

    // Tells the engine how many best lines to calculate and send to
    // the GUI (the first move in each best line is different). This
    // is the so called "MultiPV" mode.
    variation_count: usize,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,

    // `true` if the engine is thinking in pondering mode at the
    // moment.
    is_pondering: bool,

    // This helps the engine decide when to send periodic progress
    // reports.
    silent_since: SystemTime,

    // A queue for the messages send by the engine to the GUI.
    queue: VecDeque<uci::EngineReply>,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = Tt::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);

        Engine {
            tt: tt.clone(),
            position: Position::from_fen(STARTING_POSITION).ok().unwrap(),
            current_depth: 0,
            search_thread: SearchThread::new(tt),
            play_when: PlayWhen::Never,
            variation_count: 1,
            pondering_is_allowed: false,
            is_pondering: false,
            silent_since: SystemTime::now(),
            queue: VecDeque::new(),
        }
    }

    // A helper method. It it adds a progress report message to
    // `self.queue`.
    fn queue_progress_report(&mut self) {
        let &SearchStatus { depth, searched_nodes, nps, .. } = self.search_thread.status();
        self.queue.push_back(uci::EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
        ]));
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current
    // (multi)PV to `self.queue`.
    fn queue_pv(&mut self) {
        let &SearchStatus { depth, ref variations, searched_nodes, duration_millis, nps, .. } =
            self.search_thread.status();
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut moves_string = String::new();
            for m in moves {
                moves_string.push_str(&m.notation());
                moves_string.push(' ');
            }
            self.queue.push_back(uci::EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("multipv".to_string(), format!("{}", i + 1)),
            ("score".to_string(), format!("cp {}{}", value, bound_suffix)),
            ("time".to_string(), format!("{}", duration_millis)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), moves_string),
        ]));
        }
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current
    // best move to `self.queue`.
    fn queue_best_move(&mut self) {
        let &SearchStatus { ref variations, .. } = self.search_thread.status();
        self.queue.push_back(uci::EngineReply::BestMove {
            best_move: variations[0].moves.get(0).map_or("0000".to_string(), |m| m.notation()),
            ponder_move: variations[0].moves.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            "MultiPV" => {
                self.variation_count = match value.parse::<usize>().unwrap_or(0) {
                    0 => 1,
                    n if n > 500 => 500,
                    n => n,
                };
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        self.tt.clear();
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if let Ok(p) = Position::from_history(fen, moves) {
            self.position = p;
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        // Note: We ignore the "mate" parameter.

        self.search_thread.stop();
        self.tt.new_search();
        self.current_depth = 0;
        self.is_pondering = ponder;
        self.play_when = if infinite {
            PlayWhen::Never
        } else if movetime.is_some() {
            PlayWhen::MoveTime(movetime.unwrap())
        } else if nodes.is_some() {
            PlayWhen::Nodes(nodes.unwrap())
        } else if depth.is_some() {
            PlayWhen::Depth(depth.unwrap() as u8)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(&self.position,
                                                      self.pondering_is_allowed,
                                                      wtime,
                                                      btime,
                                                      winc,
                                                      binc,
                                                      movestogo))
        };
        self.silent_since = SystemTime::now();
        self.search_thread.search(&self.position, self.variation_count, searchmoves);
    }

    fn ponder_hit(&mut self) {
        if self.search_thread.status().done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.search_thread.stop();
        self.queue_best_move();
    }

    fn get_reply(&mut self) -> Option<uci::EngineReply> {
        if !self.search_thread.status().done {
            // Update the search status.
            self.search_thread.update_status();
            let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } =
                self.search_thread.status();

            // Send the (multi)PV when changed.
            if depth > self.current_depth {
                self.current_depth = depth;
                self.queue_pv();
            }

            // Send periodic progress reports.
            if self.silent_since.elapsed().unwrap().as_secs() > 10 {
                self.queue_progress_report();
            }

            // Register the updated search status with the time manager.
            if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
                tm.update_status(self.search_thread.status());
            }

            // Check if we must play now.
            if !self.is_pondering &&
               match self.play_when {
                PlayWhen::TimeManagement(ref tm) => done || tm.must_play(),
                PlayWhen::MoveTime(t) => done || duration_millis >= t,
                PlayWhen::Nodes(n) => done || searched_nodes >= n,
                PlayWhen::Depth(d) => done || depth >= d,
                PlayWhen::Never => false,
            } {
                self.stop();
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.search_thread.join();
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author(&self) -> String {
        AUTHOR.to_string()
    }

    fn options(&self) -> Vec<(uci::OptionName, uci::OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), uci::OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), uci::OptionDescription::Check { default: false }),
            ("MultiPV".to_string(), uci::OptionDescription::Spin { min: 1, max: 500, default: 1 }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

fn main() {
    if let Ok(mut uci_loop) = uci::Server::wait_for_hanshake(EngineFactory) {
        match uci_loop.serve() {
            Ok(_) => {
                exit(0);
            }
            Err(_) => {
                exit(1);
            }
        }
    }
    exit(2);
}
<|endoftext|>


<commit_before>use num_traits::ToPrimitive;
use num_bigint::BigInt;
use std::str::FromStr;
use std::cmp::max;

mod precision;
use self::precision::*;

pub mod asset_symbol;
pub use self::asset_symbol::*;

pub mod cmp;
pub use self::cmp::*;

pub mod error;
pub use self::error::*;

#[derive(Debug, Clone)]
pub struct Asset {
    pub amount: i64,
    pub decimals: u8,
    pub symbol: AssetSymbol
}

impl Asset {

    pub fn add(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_add(b)?, decimals, symbol: self.symbol })
    }

    pub fn sub(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_sub(b)?, decimals, symbol: self.symbol })
    }

    pub fn mul(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = self.decimals + other.decimals;

        let mul = i128::from(self.amount).checked_mul(i128::from(other.amount))?;
        let final_mul = set_decimals_i128(mul, decimals, precision)?;
        if final_mul > i128::from(::std::i64::MAX) { return None }
        Some(Asset {
            amount: final_mul as i64,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn div(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol || other.amount == 0 { return None }
        let decimals = max(max(self.decimals, other.decimals), precision);
        let a = set_decimals_i64(self.amount, self.decimals, decimals * 2)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset {
            amount: set_decimals_i64(a.checked_div(b)?, decimals, precision)?,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn pow(&self, num: u16, precision: u8) -> Option<Self> {
        if num == 0 {
            return Some(Asset {
                amount: set_decimals_i64(1, 0, precision)?,
                decimals: precision,
                symbol: self.symbol
            })
        }
        let big_zero = BigInt::from(0);
        let big_one = BigInt::from(1);

        let decimals = u16::from(self.decimals).checked_mul(num)?;
        let mut res = BigInt::from(1);
        {
            let mut base = BigInt::from(self.amount);
            let mut exp = BigInt::from(num);
            loop {
                if &exp & &big_one == big_one { res = &res * &base; }
                exp >>= 1;
                if exp == big_zero { break };
                base = &base * &base;
            }
        }

        res = set_decimals_big(&res, decimals, u16::from(precision));
        Some(Asset {
            amount: res.to_i64()?,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn set_decimals(&self, decimals: u8) -> Option<Self> {
        let amount = set_decimals_i64(self.amount, self.decimals, decimals)?;
        Some(Asset {
            amount,
            decimals,
            symbol: self.symbol
        })
    }

    pub fn to_str(&self) -> Box<str> {
        let mut s = self.amount.to_string();
        if self.decimals > 0 {
            let len = s.len();
            if len == self.decimals as usize { s.insert_str(0, "0."); }
            else { s.insert(len - (self.decimals as usize), '.'); }
        }
        s.push(' ');
        s.push_str(self.symbol.as_str());
        s.into_boxed_str()
    }
}

impl FromStr for Asset {
    type Err = AssetError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > 32 {
            return Err(AssetError { kind: AssetErrorKind::StrTooLarge })
        }
        let mut split = s.trim().splitn(2, ' ');

        let amount: i64;
        let mut decimals: u8 = 0;
        match split.next() {
            Some(x) => {
                if let Some(pos) = x.find('.') {
                    let len = x.len() - 1;
                    if pos > 0 { decimals = (len - pos) as u8; }
                    else { decimals = len as u8; }

                    if decimals > 8 {
                        return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }

                    amount = match x.replace('.', "").parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                } else {
                    amount = match x.parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        let symbol = match split.next() {
            Some(x) => {
                match AssetSymbol::parse_str(x) {
                    Some(x) => x,
                    None => return Err(AssetError { kind: AssetErrorKind::InvalidAssetType })
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        Ok(Asset { amount, decimals, symbol })
    }
}

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

    #[test]
    fn test_parse_valid_input() {
        let c = |asset: Asset, amount: &str, decimals: u8, symbol: AssetSymbol| {
            assert_eq!(asset.amount.to_string(), amount);
            assert_eq!(asset.decimals, decimals);
            assert_eq!(asset.symbol, symbol);
        };

        c(get_asset("1 GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset("1. GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset(".1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("-.1 GOLD"), "-1", 1, AssetSymbol::GOLD);
        c(get_asset("0.1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("1.0 SILVER"), "10", 1, AssetSymbol::SILVER);
        c(get_asset("0 SILVER"), "0", 0, AssetSymbol::SILVER);
        c(get_asset("-0.0 SILVER"), "0", 1, AssetSymbol::SILVER);
        c(get_asset("-1.0 SILVER"), "-10", 1, AssetSymbol::SILVER);
    }

    #[test]
    fn test_fail_parsing_invalid_input() {
        let c = |asset: &str, err: AssetErrorKind| {
            let e = Asset::from_str(asset).err().unwrap();
            assert_eq!(e.kind, err);
        };

        c("1e10 GOLD", AssetErrorKind::InvalidAmount);
        c("a100 GOLD", AssetErrorKind::InvalidAmount);
        c("100a GOLD", AssetErrorKind::InvalidAmount);

        c("1234567890123456789012345678 GOLD", AssetErrorKind::StrTooLarge);
        c("1", AssetErrorKind::InvalidFormat);

        c("1.0 GOLD a", AssetErrorKind::InvalidAssetType);
        c("1.0 gold", AssetErrorKind::InvalidAssetType);
    }

    #[test]
    fn test_set_precision() {
        let a = get_asset("1.5678 GOLD");
        assert_eq!(a.decimals, 4);
        assert_eq!(a.amount.to_string(), "15678");

        let a = a.set_decimals(2).unwrap();

        assert_eq!(a.decimals, 2);
        assert_eq!(a.amount.to_string(), "156");

        let a = a.set_decimals(0).unwrap();
        assert_eq!(a.decimals, 0);
        assert_eq!(a.amount.to_string(), "1");

        let a = a.set_decimals(4).unwrap();
        assert_eq!(a.decimals, 4);
        assert_eq!(a.amount.to_string(), "10000");
    }

    #[test]
    fn test_perform_arithmetic() {
        let c = |asset: &Asset, amount: &str| {
            assert_eq!(&*asset.to_str(), amount);
        };

        let a = get_asset("123.456 GOLD");
        c(&a.add(&get_asset("2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.add(&get_asset("-2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.add(&get_asset(".00000001 GOLD")).unwrap(), "123.45600001 GOLD");
        c(&a.sub(&get_asset("2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.sub(&get_asset("-2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.mul(&get_asset("100000.11111111 GOLD"), 8).unwrap(), "12345613.71733319 GOLD");
        c(&a.mul(&get_asset("-100000.11111111 GOLD"), 8).unwrap(), "-12345613.71733319 GOLD");
        c(&a.div(&get_asset("23 GOLD"), 3).unwrap(), "5.367 GOLD");
        c(&a.div(&get_asset("-23 GOLD"), 8).unwrap(), "-5.36765217 GOLD");
        c(&a.pow(2, 8).unwrap(), "15241.38393600 GOLD");
        c(&a.pow(3, 8).unwrap(), "1881640.29520281 GOLD");
        c(&a, "123.456 GOLD");

        c(&get_asset("1.0002 GOLD").pow(1000, 8).unwrap(), "1.22137833 GOLD");
        c(&get_asset("10 GOLD").div(&get_asset("2 GOLD"), 0).unwrap(), "5 GOLD");
        c(&get_asset("5 GOLD").div(&get_asset("10 GOLD"), 1).unwrap(), "0.5 GOLD");

        assert!(&a.div(&get_asset("0 GOLD"), 1).is_none());
    }

    #[test]
    fn test_invalid_arithmetic() {
        let a = &get_asset("10 GOLD");
        let b = &get_asset("10 SILVER");

        assert!(a.add(b).is_none());
        assert!(a.sub(b).is_none());
        assert!(a.div(b, 8).is_none());
        assert!(a.mul(b, 8).is_none());
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}

<commit_msg>Fix missing padding when encoding an asset with a value >0 and <1<commit_after>use num_traits::ToPrimitive;
use num_bigint::BigInt;
use std::str::FromStr;
use std::cmp::max;

mod precision;
use self::precision::*;

pub mod asset_symbol;
pub use self::asset_symbol::*;

pub mod cmp;
pub use self::cmp::*;

pub mod error;
pub use self::error::*;

#[derive(Debug, Clone)]
pub struct Asset {
    pub amount: i64,
    pub decimals: u8,
    pub symbol: AssetSymbol
}

impl Asset {

    pub fn add(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_add(b)?, decimals, symbol: self.symbol })
    }

    pub fn sub(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_sub(b)?, decimals, symbol: self.symbol })
    }

    pub fn mul(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = self.decimals + other.decimals;

        let mul = i128::from(self.amount).checked_mul(i128::from(other.amount))?;
        let final_mul = set_decimals_i128(mul, decimals, precision)?;
        if final_mul > i128::from(::std::i64::MAX) { return None }
        Some(Asset {
            amount: final_mul as i64,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn div(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol || other.amount == 0 { return None }
        let decimals = max(max(self.decimals, other.decimals), precision);
        let a = set_decimals_i64(self.amount, self.decimals, decimals * 2)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset {
            amount: set_decimals_i64(a.checked_div(b)?, decimals, precision)?,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn pow(&self, num: u16, precision: u8) -> Option<Self> {
        if num == 0 {
            return Some(Asset {
                amount: set_decimals_i64(1, 0, precision)?,
                decimals: precision,
                symbol: self.symbol
            })
        }
        let big_zero = BigInt::from(0);
        let big_one = BigInt::from(1);

        let decimals = u16::from(self.decimals).checked_mul(num)?;
        let mut res = BigInt::from(1);
        {
            let mut base = BigInt::from(self.amount);
            let mut exp = BigInt::from(num);
            loop {
                if &exp & &big_one == big_one { res = &res * &base; }
                exp >>= 1;
                if exp == big_zero { break };
                base = &base * &base;
            }
        }

        res = set_decimals_big(&res, decimals, u16::from(precision));
        Some(Asset {
            amount: res.to_i64()?,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn set_decimals(&self, decimals: u8) -> Option<Self> {
        let amount = set_decimals_i64(self.amount, self.decimals, decimals)?;
        Some(Asset {
            amount,
            decimals,
            symbol: self.symbol
        })
    }

    pub fn to_str(&self) -> Box<str> {
        let mut s = self.amount.to_string();
        if self.decimals > 0 {
            let len = s.len();
            if len < self.decimals as usize {
                let start = if self.amount < 0 { 1 } else { 0 };
                let diff = self.decimals as usize - len + start;
                s.insert_str(start, "0.");
                s.insert_str(start + 2, &"0".repeat(diff));
            } else if len == self.decimals as usize {
                s.insert_str(0, "0.");
            } else {
                s.insert(len - (self.decimals as usize), '.');
            }
        }
        s.push(' ');
        s.push_str(self.symbol.as_str());
        s.into_boxed_str()
    }
}

impl FromStr for Asset {
    type Err = AssetError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > 32 {
            return Err(AssetError { kind: AssetErrorKind::StrTooLarge })
        }
        let mut split = s.trim().splitn(2, ' ');

        let amount: i64;
        let mut decimals: u8 = 0;
        match split.next() {
            Some(x) => {
                if let Some(pos) = x.find('.') {
                    let len = x.len() - 1;
                    if pos > 0 { decimals = (len - pos) as u8; }
                    else { decimals = len as u8; }

                    if decimals > 8 {
                        return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }

                    amount = match x.replace('.', "").parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                } else {
                    amount = match x.parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        let symbol = match split.next() {
            Some(x) => {
                match AssetSymbol::parse_str(x) {
                    Some(x) => x,
                    None => return Err(AssetError { kind: AssetErrorKind::InvalidAssetType })
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        Ok(Asset { amount, decimals, symbol })
    }
}

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

    #[test]
    fn test_parse_valid_input() {
        let c = |asset: Asset, amount: &str, decimals: u8, symbol: AssetSymbol| {
            assert_eq!(asset.amount.to_string(), amount);
            assert_eq!(asset.decimals, decimals);
            assert_eq!(asset.symbol, symbol);
        };

        c(get_asset("1 GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset("1. GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset(".1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("-.1 GOLD"), "-1", 1, AssetSymbol::GOLD);
        c(get_asset("0.1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("1.0 SILVER"), "10", 1, AssetSymbol::SILVER);
        c(get_asset("0 SILVER"), "0", 0, AssetSymbol::SILVER);
        c(get_asset("-0.0 SILVER"), "0", 1, AssetSymbol::SILVER);
        c(get_asset("-1.0 SILVER"), "-10", 1, AssetSymbol::SILVER);
    }

    #[test]
    fn test_asset_to_str() {
        let c = |asset: Asset, s: &str| {
            assert_eq!(&*asset.to_str(), s);
        };
        c(get_asset("1.00001 GOLD"), "1.00001 GOLD");
        c(get_asset("0.00001 GOLD"), "0.00001 GOLD");
        c(get_asset("-0.00001 GOLD"), "-0.00001 GOLD");
        c(get_asset(".00001 GOLD"), "0.00001 GOLD");
        c(get_asset(".1 GOLD"), "0.1 GOLD");
        c(get_asset("1.0 GOLD"), "1.0 GOLD");
    }

    #[test]
    fn test_fail_parsing_invalid_input() {
        let c = |asset: &str, err: AssetErrorKind| {
            let e = Asset::from_str(asset).err().unwrap();
            assert_eq!(e.kind, err);
        };

        c("1e10 GOLD", AssetErrorKind::InvalidAmount);
        c("a100 GOLD", AssetErrorKind::InvalidAmount);
        c("100a GOLD", AssetErrorKind::InvalidAmount);

        c("1234567890123456789012345678 GOLD", AssetErrorKind::StrTooLarge);
        c("1", AssetErrorKind::InvalidFormat);

        c("1.0 GOLD a", AssetErrorKind::InvalidAssetType);
        c("1.0 gold", AssetErrorKind::InvalidAssetType);
    }

    #[test]
    fn test_set_precision() {
        let a = get_asset("1.5678 GOLD");
        assert_eq!(a.decimals, 4);
        assert_eq!(a.amount.to_string(), "15678");

        let a = a.set_decimals(2).unwrap();

        assert_eq!(a.decimals, 2);
        assert_eq!(a.amount.to_string(), "156");

        let a = a.set_decimals(0).unwrap();
        assert_eq!(a.decimals, 0);
        assert_eq!(a.amount.to_string(), "1");

        let a = a.set_decimals(4).unwrap();
        assert_eq!(a.decimals, 4);
        assert_eq!(a.amount.to_string(), "10000");
    }

    #[test]
    fn test_perform_arithmetic() {
        let c = |asset: &Asset, amount: &str| {
            assert_eq!(&*asset.to_str(), amount);
        };

        let a = get_asset("123.456 GOLD");
        c(&a.add(&get_asset("2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.add(&get_asset("-2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.add(&get_asset(".00000001 GOLD")).unwrap(), "123.45600001 GOLD");
        c(&a.sub(&get_asset("2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.sub(&get_asset("-2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.mul(&get_asset("100000.11111111 GOLD"), 8).unwrap(), "12345613.71733319 GOLD");
        c(&a.mul(&get_asset("-100000.11111111 GOLD"), 8).unwrap(), "-12345613.71733319 GOLD");
        c(&a.div(&get_asset("23 GOLD"), 3).unwrap(), "5.367 GOLD");
        c(&a.div(&get_asset("-23 GOLD"), 8).unwrap(), "-5.36765217 GOLD");
        c(&a.pow(2, 8).unwrap(), "15241.38393600 GOLD");
        c(&a.pow(3, 8).unwrap(), "1881640.29520281 GOLD");
        c(&a, "123.456 GOLD");

        c(&get_asset("1.0002 GOLD").pow(1000, 8).unwrap(), "1.22137833 GOLD");
        c(&get_asset("10 GOLD").div(&get_asset("2 GOLD"), 0).unwrap(), "5 GOLD");
        c(&get_asset("5 GOLD").div(&get_asset("10 GOLD"), 1).unwrap(), "0.5 GOLD");

        assert!(&a.div(&get_asset("0 GOLD"), 1).is_none());
    }

    #[test]
    fn test_invalid_arithmetic() {
        let a = &get_asset("10 GOLD");
        let b = &get_asset("10 SILVER");

        assert!(a.add(b).is_none());
        assert!(a.sub(b).is_none());
        assert!(a.div(b, 8).is_none());
        assert!(a.mul(b, 8).is_none());
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}

<|endoftext|>


<commit_before>use glium::{Frame, VertexBuffer, Blend, Surface};
use glium::Display;
use glium::index::{IndexBuffer, PrimitiveType};
use glium::draw_parameters::DrawParameters;
use glium::draw_parameters::LinearBlendingFactor::*;
use glium::draw_parameters::BlendingFunction::*;
use glium::uniforms::{Uniforms, UniformsStorage, AsUniformValue, MagnifySamplerFilter, MinifySamplerFilter};

use nalgebra::Norm;

use {GameContext, Node, Vect};
use super::{RenderContext, Vertex, vert};
use math::*;
use quantum_circuit_wars::circuit::{GameView, Game, Port};

fn input_pos(gen: &Game<Node>, input: Port<u32>, _size: f32) -> Vect {
    let node = gen.get(input.node).unwrap();
    let pos = node.1.pos;
    let percent = (input.port + 1) as f32 / (node.0.max_in() + 1) as f32;
    Vect::new(pos[0] - 0.5 + percent, -(pos[1] - 0.5))
}

fn output_pos(gen: &Game<Node>, output: Port<u32>, size: f32) -> Vect {
    let node = gen.get(output.node).unwrap();
    let pos = node.1.pos;
    let percent = (output.port + 1) as f32 / (node.0.max_out() + 1) as f32;
    Vect::new(pos[0] - 0.5 + percent, -(pos[1] + 0.5 + size))
}

pub fn render(display: &Display, rctx: &mut RenderContext, world: GameView<Node>, ctx: &GameContext) {
    let mut target = display.draw();
    target.clear_color(0.0157, 0.0173, 0.0204, 1.);
    let draw_params = DrawParameters {
        blend: Blend {
            color: Addition {
                source: SourceAlpha,
                destination: OneMinusSourceAlpha,
            },
            alpha: Addition {
                source: SourceAlpha,
                destination: OneMinusSourceAlpha,
            },
            constant_value: (0f32, 0f32, 0f32, 1f32),
        },
        smooth: None,
        ..Default::default()
    };
    let dims = display.get_framebuffer_dimensions();
    for (_, data) in world.iter() {
        let pos = flip_y(data.pos);
        let corner_pos = pos - Vect::new(ctx.node_width, ctx.node_width) * 0.5;
        let matrix = rctx.cam * translation(corner_pos.x, corner_pos.y);
        let uniforms = uniform! {
            matrix: *matrix.as_ref(),
        };
        draw(&mut target, &rctx, "back", "plain", &uniforms, &draw_params);
        let matrix = rctx.cam * translation(corner_pos.x + 0.05, corner_pos.y + 0.05) * scale(0.9, 0.9);
        let program = rctx.programs.get("plain");
        let program = program.as_ref().expect("Node didn't have shader.");
        let uniforms = uniform! {
            matrix: *matrix.as_ref(),
        };
        let model = rctx.models.get("node").unwrap();
        target.draw(&model.vertices, &model.indices, &program, &uniforms, &draw_params).expect("Drawing node failed.");

        let mut draw = |things: &[_]| {
            for p in things {
                let p = pos + flip_y(*p) - Vect::new(ctx.port_size, ctx.port_size) * 0.5;
                let matrix = rctx.cam * translation(p.x, p.y) * scale(ctx.port_size, ctx.port_size);
                let uniforms = uniform! {
                    matrix: *matrix.as_ref(),
                };
                draw(&mut target, &rctx, "node", "plain", &uniforms, &draw_params);
            }
        };
        draw(&data.outputs.borrow());
        draw(&data.inputs.borrow());
    }
    let mut lines = Vec::with_capacity(world.connections());
    for (src, trg) in world.iter_connections() {
        let src = output_pos(&world, src, ctx.port_size);
        let trg = input_pos(&world, trg, ctx.port_size);
        let trg = Vect::new(trg[0], trg[1] + ctx.port_size);
        add_arrow(&mut lines, src, trg, 0.1, 0.1 * TAU);
    }
    let vertices = VertexBuffer::new(display, &lines).unwrap();
    let indices = (0..lines.len() as u32).collect::<Vec<_>>();
    let indices = IndexBuffer::new(display, PrimitiveType::LinesList, &indices).unwrap();
    let matrix = rctx.cam * translation(0., 0.);
    let uniforms = uniform! {
        matrix: *matrix.as_ref(),
    };
    let program = rctx.programs.get("plain").unwrap();
    target.draw(&vertices, &indices, program, &uniforms, &draw_params).unwrap();
    target.finish().unwrap();
}

pub fn render_splashscreen(render_context: &mut RenderContext) {
    let mut target = display.draw();
    let draw_params = DrawParameters {
        blend: Blend {
            color: Addition {
                source: SourceAlpha,
                destination: OneMinusSourceAlpha,
            },
            alpha: Addition {
                source: SourceAlpha,
                destination: OneMinusSourceAlpha,
            },
            constant_value: (0f32, 0f32, 0f32, 1f32),
        },
        smooth: None,
        ..Default::default()
    };
    let texture = render_context.textures.get("splash");
    let model = render_context.models.get("node");
    let program = render_context.programs.get("plain");
    let splash_matrix = render_context.cam * math::translation(0., 0.) * scale(ctx.port_size, crtx.port_size);
    let uniforms = uniform! {
        matrix: *matrix.as_ref(),
        tex: texture.sampled()
            .magnify_filter(MagnifySamplerFilter::Nearest)
            .minify_filter(MinifySamplerFilter::Nearest),

    };
    target.draw(&model.vertices, &model.indices, program, uniforms, draw_params);
}

fn draw<A, B>(target: &mut Frame, rctx: &RenderContext, model: &str, program: &str, uniforms: &UniformsStorage<A, B>, draw_params: &DrawParameters)
    where A: AsUniformValue,
          B: Uniforms,
{
    let model = rctx.models.get(model).unwrap();
    let program = rctx.programs.get(program).unwrap();
    target.draw(&model.vertices, &model.indices, program, uniforms, draw_params).unwrap();
}

fn add_arrow(lines: &mut Vec<Vertex>, src: Vect, trg: Vect, len: f32, theta: f32) {
    lines.push(vert(src.x, src.y));
    lines.push(vert(trg.x, trg.y));
    let vec = (src - trg).normalize();
    let mut add_part = |theta: f32| {
        let (sin, cos) = theta.sin_cos();
        let arrow = Vect::new(
            vec.x * cos - vec.y * sin,
            vec.x * sin + vec.y * cos);
        lines.push(vert(trg.x, trg.y));
        let v = trg + (arrow * len);
        lines.push(vert(v.x, v.y));
    };
    add_part(theta);
    add_part(-theta);
}
<commit_msg>some errors fixed.<commit_after>use glium::{Frame, VertexBuffer, Blend, Surface};
use glium::Display;
use glium::index::{IndexBuffer, PrimitiveType};
use glium::draw_parameters::DrawParameters;
use glium::draw_parameters::LinearBlendingFactor::*;
use glium::draw_parameters::BlendingFunction::*;
use glium::uniforms::{Uniforms, UniformsStorage, AsUniformValue, MagnifySamplerFilter, MinifySamplerFilter};

use nalgebra::Norm;

use {GameContext, Node, Vect};
use super::{RenderContext, Vertex, vert};
use math::*;
use quantum_circuit_wars::circuit::{GameView, Game, Port};

fn input_pos(gen: &Game<Node>, input: Port<u32>, _size: f32) -> Vect {
    let node = gen.get(input.node).unwrap();
    let pos = node.1.pos;
    let percent = (input.port + 1) as f32 / (node.0.max_in() + 1) as f32;
    Vect::new(pos[0] - 0.5 + percent, -(pos[1] - 0.5))
}

fn output_pos(gen: &Game<Node>, output: Port<u32>, size: f32) -> Vect {
    let node = gen.get(output.node).unwrap();
    let pos = node.1.pos;
    let percent = (output.port + 1) as f32 / (node.0.max_out() + 1) as f32;
    Vect::new(pos[0] - 0.5 + percent, -(pos[1] + 0.5 + size))
}

pub fn render(display: &Display, rctx: &mut RenderContext, world: GameView<Node>, ctx: &GameContext) {
    let mut target = display.draw();
    target.clear_color(0.0157, 0.0173, 0.0204, 1.);
    let draw_params = DrawParameters {
        blend: Blend {
            color: Addition {
                source: SourceAlpha,
                destination: OneMinusSourceAlpha,
            },
            alpha: Addition {
                source: SourceAlpha,
                destination: OneMinusSourceAlpha,
            },
            constant_value: (0f32, 0f32, 0f32, 1f32),
        },
        smooth: None,
        ..Default::default()
    };
    let dims = display.get_framebuffer_dimensions();
    for (_, data) in world.iter() {
        let pos = flip_y(data.pos);
        let corner_pos = pos - Vect::new(ctx.node_width, ctx.node_width) * 0.5;
        let matrix = rctx.cam * translation(corner_pos.x, corner_pos.y);
        let uniforms = uniform! {
            matrix: *matrix.as_ref(),
        };
        draw(&mut target, &rctx, "back", "plain", &uniforms, &draw_params);
        let matrix = rctx.cam * translation(corner_pos.x + 0.05, corner_pos.y + 0.05) * scale(0.9, 0.9);
        let program = rctx.programs.get("plain");
        let program = program.as_ref().expect("Node didn't have shader.");
        let uniforms = uniform! {
            matrix: *matrix.as_ref(),
        };
        let model = rctx.models.get("node").unwrap();
        target.draw(&model.vertices, &model.indices, &program, &uniforms, &draw_params).expect("Drawing node failed.");

        let mut draw = |things: &[_]| {
            for p in things {
                let p = pos + flip_y(*p) - Vect::new(ctx.port_size, ctx.port_size) * 0.5;
                let matrix = rctx.cam * translation(p.x, p.y) * scale(ctx.port_size, ctx.port_size);
                let uniforms = uniform! {
                    matrix: *matrix.as_ref(),
                };
                draw(&mut target, &rctx, "node", "plain", &uniforms, &draw_params);
            }
        };
        draw(&data.outputs.borrow());
        draw(&data.inputs.borrow());
    }
    let mut lines = Vec::with_capacity(world.connections());
    for (src, trg) in world.iter_connections() {
        let src = output_pos(&world, src, ctx.port_size);
        let trg = input_pos(&world, trg, ctx.port_size);
        let trg = Vect::new(trg[0], trg[1] + ctx.port_size);
        add_arrow(&mut lines, src, trg, 0.1, 0.1 * TAU);
    }
    let vertices = VertexBuffer::new(display, &lines).unwrap();
    let indices = (0..lines.len() as u32).collect::<Vec<_>>();
    let indices = IndexBuffer::new(display, PrimitiveType::LinesList, &indices).unwrap();
    let matrix = rctx.cam * translation(0., 0.);
    let uniforms = uniform! {
        matrix: *matrix.as_ref(),
    };
    let program = rctx.programs.get("plain").unwrap();
    target.draw(&vertices, &indices, program, &uniforms, &draw_params).unwrap();
    target.finish().unwrap();
}

pub fn render_splashscreen(render_context: &mut RenderContext) {
    let mut target = display.draw();
    let draw_params = DrawParameters {
        blend: Blend {
            color: Addition {
                source: SourceAlpha,
                destination: OneMinusSourceAlpha,
            },
            alpha: Addition {
                source: SourceAlpha,
                destination: OneMinusSourceAlpha,
            },
            constant_value: (0f32, 0f32, 0f32, 1f32),
        },
        smooth: None,
        ..Default::default()
    };
    let texture = render_context.textures.get("splash");
    let model = render_context.models.get("node").unwrap();
    let program = render_context.programs.get("plain");
    let splash_matrix = render_context.cam * math::translation(0., 0.) * scale(ctx.port_size, crtx.port_size);
    let uniforms = uniform! {
        matrix: *matrix.as_ref(),
        tex: texture.sampled()
            .magnify_filter(MagnifySamplerFilter::Nearest)
            .minify_filter(MinifySamplerFilter::Nearest),

    };
    target.draw(&model.vertices, &model.indices, program, uniforms, draw_params);
}

fn draw<A, B>(target: &mut Frame, rctx: &RenderContext, model: &str, program: &str, uniforms: &UniformsStorage<A, B>, draw_params: &DrawParameters)
    where A: AsUniformValue,
          B: Uniforms,
{
    let model = rctx.models.get(model).unwrap();
    let program = rctx.programs.get(program).unwrap();
    target.draw(&model.vertices, &model.indices, program, uniforms, draw_params).unwrap();
}

fn add_arrow(lines: &mut Vec<Vertex>, src: Vect, trg: Vect, len: f32, theta: f32) {
    lines.push(vert(src.x, src.y));
    lines.push(vert(trg.x, trg.y));
    let vec = (src - trg).normalize();
    let mut add_part = |theta: f32| {
        let (sin, cos) = theta.sin_cos();
        let arrow = Vect::new(
            vec.x * cos - vec.y * sin,
            vec.x * sin + vec.y * cos);
        lines.push(vert(trg.x, trg.y));
        let v = trg + (arrow * len);
        lines.push(vert(v.x, v.y));
    };
    add_part(theta);
    add_part(-theta);
}
<|endoftext|>


<commit_before>use super::*;
use std::cmp::Ordering;

pointer_ord!(WordInstance);

impl WordInstance {
    pub fn next_instance(&self) -> Option<InstanceCell> {
        let mb = self.message.borrow();
        mb.instances.get((self.index as isize + 1) as usize).cloned()
    }

    pub fn prev_instance(&self) -> Option<InstanceCell> {
        let mb = self.message.borrow();
        mb.instances.get((self.index as isize - 1) as usize).cloned()
    }

    pub fn precoincidence_neighbors(ins: (&InstanceCell, &InstanceCell), min_edge_distance: usize, words_only_distance: usize) -> bool {
        let bs = (ins.0.borrow(), ins.1.borrow());
        let ms = (bs.0.message.borrow(), bs.1.message.borrow());
        let preins = (ms.0.instances.get((bs.0.index as isize - 1) as usize),
                ms.1.instances.get((bs.1.index as isize - 1) as usize));

        match preins {
            // There are two words
            (Some(i0), Some(i1)) => {
                if Category::are_postcocategories((&i0.borrow().category, &i1.borrow().category)) ||
                    Category::are_precocategories((&i0.borrow().category, &i1.borrow().category)) {
                    true
                } else if i0.borrow().word == i1.borrow().word {
                    if words_only_distance == 0 {
                        true
                    } else {
                        WordInstance::precoincidence_neighbors((i0, i1),
                            min_edge_distance.saturating_sub(1),
                            words_only_distance.saturating_sub(1))
                    }
                } else {
                    false
                }
            },
            // End of the sentence is only a match if we had prior matches
            (None, None) => min_edge_distance == 0,
            _ => false,
        }
    }

    pub fn postcoincidence_neighbors(ins: (&InstanceCell, &InstanceCell), min_edge_distance: usize, words_only_distance: usize) -> bool {
        let bs = (ins.0.borrow(), ins.1.borrow());
        let ms = (bs.0.message.borrow(), bs.1.message.borrow());
        let postins = (ms.0.instances.get((bs.0.index as isize + 1) as usize),
                ms.1.instances.get((bs.1.index as isize + 1) as usize));

        match postins {
            // There are two words
            (Some(i0), Some(i1)) => {
                if Category::are_precocategories((&i0.borrow().category, &i1.borrow().category)) ||
                    Category::are_postcocategories((&i0.borrow().category, &i1.borrow().category)) {
                    true
                } else if i0.borrow().word == i1.borrow().word {
                    if words_only_distance == 0 {
                        true
                    } else {
                        WordInstance::postcoincidence_neighbors((i0, i1),
                            min_edge_distance.saturating_sub(1),
                            words_only_distance.saturating_sub(1))
                    }
                } else {
                    false
                }
            },
            // End of the sentence is only a match if this can be the edge
            (None, None) => min_edge_distance == 0,
            _ => false,
        }
    }
}
<commit_msg>minor adjustment to category learning<commit_after>use super::*;
use std::cmp::Ordering;

pointer_ord!(WordInstance);

impl WordInstance {
    pub fn next_instance(&self) -> Option<InstanceCell> {
        let mb = self.message.borrow();
        mb.instances.get((self.index as isize + 1) as usize).cloned()
    }

    pub fn prev_instance(&self) -> Option<InstanceCell> {
        let mb = self.message.borrow();
        mb.instances.get((self.index as isize - 1) as usize).cloned()
    }

    pub fn precoincidence_neighbors(ins: (&InstanceCell, &InstanceCell), min_edge_distance: usize, words_only_distance: usize) -> bool {
        let bs = (ins.0.borrow(), ins.1.borrow());
        let ms = (bs.0.message.borrow(), bs.1.message.borrow());
        let preins = (ms.0.instances.get((bs.0.index as isize - 1) as usize),
                ms.1.instances.get((bs.1.index as isize - 1) as usize));

        match preins {
            // There are two words
            (Some(i0), Some(i1)) => {
                if Category::are_precocategories((&i0.borrow().category, &i1.borrow().category)) {
                    true
                } else if i0.borrow().word == i1.borrow().word {
                    if words_only_distance == 0 {
                        true
                    } else {
                        WordInstance::precoincidence_neighbors((i0, i1),
                            min_edge_distance.saturating_sub(1),
                            words_only_distance.saturating_sub(1))
                    }
                } else {
                    false
                }
            },
            // End of the sentence is only a match if we had prior matches
            (None, None) => min_edge_distance == 0,
            _ => false,
        }
    }

    pub fn postcoincidence_neighbors(ins: (&InstanceCell, &InstanceCell), min_edge_distance: usize, words_only_distance: usize) -> bool {
        let bs = (ins.0.borrow(), ins.1.borrow());
        let ms = (bs.0.message.borrow(), bs.1.message.borrow());
        let postins = (ms.0.instances.get((bs.0.index as isize + 1) as usize),
                ms.1.instances.get((bs.1.index as isize + 1) as usize));

        match postins {
            // There are two words
            (Some(i0), Some(i1)) => {
                if Category::are_postcocategories((&i0.borrow().category, &i1.borrow().category)) {
                    true
                } else if i0.borrow().word == i1.borrow().word {
                    if words_only_distance == 0 {
                        true
                    } else {
                        WordInstance::postcoincidence_neighbors((i0, i1),
                            min_edge_distance.saturating_sub(1),
                            words_only_distance.saturating_sub(1))
                    }
                } else {
                    false
                }
            },
            // End of the sentence is only a match if this can be the edge
            (None, None) => min_edge_distance == 0,
            _ => false,
        }
    }
}
<|endoftext|>


<commit_before>use num::complex::Complex;
use super::Simd;
use simd::x86::avx::{f32x8,f64x4,AvxF32x8,AvxF64x4};

pub type Reg32 = f32x8;

pub type Reg64 = f64x4;

impl Simd<f32> for f32x8
{
    type Array = [f32; 8];

    #[inline]
    fn to_array(self) -> Self::Array {
        let mut target = [0.0; 8];
        self.store(&mut target, 0);
        target
    }

    type ComplexArray = [Complex<f32>; 4];

    #[inline]
    fn len() -> usize {
        8
    }

    #[inline]
    fn load_wrap_unchecked(array: &[f32], idx: usize) -> f32x8 {
        let mut temp = [0.0; 8];
        for i in 0..temp.len() {
            temp[i] = unsafe {*array.get_unchecked((idx + i) % array.len())};
        }
        f32x8::load(&temp, 0)
    }

    #[inline]
    fn from_complex(value: Complex<f32>) -> f32x8 {
        f32x8::new(value.re, value.im, value.re, value.im,
                   value.re, value.im, value.re, value.im)
    }

    #[inline]
    fn add_real(self, value: f32) -> f32x8
    {
        let increment = f32x8::splat(value);
        self + increment
    }

    #[inline]
    fn add_complex(self, value: Complex<f32>) -> f32x8
    {
        let increment = f32x8::from_complex(value);
        self + increment
    }

    #[inline]
    fn scale_real(self, value: f32) -> f32x8
    {
        let scale_vector = f32x8::splat(value);
        self * scale_vector
    }

    #[inline]
    fn scale_complex(self, value: Complex<f32>) -> f32x8
    {
        let scaling_real = f32x8::splat(value.re);
        let scaling_imag = f32x8::splat(value.im);
        let parallel = scaling_real * self;
        // There should be a shufps operation which shuffles the vector self
        let shuffled = f32x8::new(self.extract(1), self.extract(0), self.extract(3), self.extract(2),
                                  self.extract(5), self.extract(4), self.extract(7), self.extract(6));
        let cross = scaling_imag * shuffled;
        parallel.addsub(cross)
    }

    #[inline]
    fn mul_complex(self, value: f32x8) -> f32x8
    {
        let scaling_real = f32x8::new(value.extract(0), value.extract(0), value.extract(2), value.extract(2),
                                      value.extract(4), value.extract(4), value.extract(6), value.extract(6));
        let scaling_imag = f32x8::new(value.extract(1), value.extract(1), value.extract(3), value.extract(3),
                                      value.extract(5), value.extract(5), value.extract(7), value.extract(7));
        let parallel = scaling_real * self;
        // There should be a shufps operation which shuffles the vector self
        let shuffled = f32x8::new(self.extract(1), self.extract(0), self.extract(3), self.extract(2),
                                  self.extract(5), self.extract(4), self.extract(7), self.extract(6));
        let cross = scaling_imag * shuffled;
        parallel.addsub(cross)
    }

    #[inline]
    fn div_complex(self, value: f32x8) -> f32x8
    {
                let scaling_imag = f32x8::new(self.extract(0), self.extract(0), self.extract(2), self.extract(2),
                                      self.extract(4), self.extract(4), self.extract(6), self.extract(6));
                let scaling_real = f32x8::new(self.extract(1), self.extract(1), self.extract(3), self.extract(3),
                                      self.extract(5), self.extract(5), self.extract(7), self.extract(7));
                let parallel = scaling_real * value;
                // There should be a shufps operation which shuffles the vector self
                let shuffled = f32x8::new(value.extract(1), value.extract(0), value.extract(3), value.extract(2),
                                  value.extract(5), value.extract(4), value.extract(7), value.extract(6));
                let cross = scaling_imag * shuffled;
                let mul = parallel.addsub(cross);
                let square = shuffled * shuffled;
                let square_shuffled = f32x8::new(square.extract(1), square.extract(0), square.extract(3), square.extract(2),
                                         square.extract(5), square.extract(4), square.extract(7), square.extract(6));
                let sum = square + square_shuffled;
                let div = mul / sum;
                f32x8::new(div.extract(1), div.extract(0), div.extract(3), div.extract(2),
                   div.extract(5), div.extract(4), div.extract(7), div.extract(6))
    }

    #[inline]
    fn complex_abs_squared(self) -> f32x8
    {
        let squared = self * self;
        squared.hadd(squared)
    }

    #[inline]
    fn complex_abs(self) -> f32x8
    {
        let squared_sum = self.complex_abs_squared();
        AvxF32x8::sqrt(squared_sum)
    }

    #[inline]
    fn complex_abs_squared2(self) -> f32x8
    {
        self.complex_abs_squared()
    }

    #[inline]
    fn complex_abs2(self) -> f32x8
    {
        self.complex_abs()
    }

    #[inline]
    fn sqrt(self) -> f32x8 {
        AvxF32x8::sqrt(self)
    }

    #[inline]
    fn store_half_unchecked(self, target: &mut [f32], index: usize)
    {
        unsafe {
            *target.get_unchecked_mut(index) = self.extract(0);
            *target.get_unchecked_mut(index+ 1) = self.extract(1);
            *target.get_unchecked_mut(index + 2) = self.estract(2);
            *target.get_unchecked_mut(index + 3) = self.extract(3);
        }
    }

    #[inline]
    fn sum_real(&self) -> f32 {
        self.extract(0) +
        self.extract(1) +
        self.extract(2) +
        self.extract(3) +
        self.extract(4) +
        self.extract(5) +
        self.extract(6) +
        self.extract(7)
    }

    #[inline]
    fn sum_complex(&self) -> Complex<f32> {
        Complex::<f32>::new(self.extract(0) + self.extract(2) + self.extract(4) + self.extract(6),
                            self.extract(1) + self.extract(3) + self.extract(5) + self.extract(7))
    }
}

impl Simd<f64> for f64x4
{
    type Array = [f64; 4];

    #[inline]
    fn to_array(self) -> Self::Array {
        let mut target = [0.0; 4];
        self.store(&mut target, 0);
        target
    }

    type ComplexArray = [Complex<f64>; 2];

    #[inline]
    fn len() -> usize {
        4
    }

    #[inline]
    fn load_wrap_unchecked(array: &[f64], idx: usize) -> f64x4 {
        let mut temp = [0.0; 4];
        for i in 0..temp.len() {
            temp[i] = unsafe { *array.get_unchecked((idx + i) % array.len()) };
        }
        f64x4::load(&temp, 0)
    }

    #[inline]
    fn from_complex(value: Complex<f64>) -> f64x4 {
        f64x4::new(value.re, value.im, value.re, value.im)
    }

    #[inline]
    fn add_real(self, value: f64) -> f64x4
    {
        let increment = f64x4::splat(value);
        self + increment
    }

    #[inline]
    fn add_complex(self, value: Complex<f64>) -> f64x4
    {
        let increment = f64x4::new(value.re, value.im, value.re, value.im);
        self + increment
    }

    #[inline]
    fn scale_real(self, value: f64) -> f64x4
    {
        let scale_vector = f64x4::splat(value);
        self * scale_vector
    }

    #[inline]
    fn scale_complex(self, value: Complex<f64>) -> f64x4
    {
        let scaling_real = f64x4::splat(value.re);
        let scaling_imag = f64x4::splat(value.im);
        let parallel = scaling_real * self;
        // There should be a shufps operation which shuffles the vector self
        let shuffled = f64x4::new(self.extract(1), self.extract(0), self.extract(3), self.extract(2));
        let cross = scaling_imag * shuffled;
        parallel.addsub(cross)
    }

    #[inline]
    fn mul_complex(self, value: f64x4) -> f64x4
    {
        let scaling_real = f64x4::new(value.extract(0), value.extract(0), value.extract(2), value.extract(2));
        let scaling_imag = f64x4::new(value.extract(1), value.extract(1), value.extract(3), value.extract(3));
        let parallel = scaling_real * self;
        // There should be a shufps operation which shuffles the vector self
        let shuffled = f64x4::new(self.extract(1), self.extract(0), self.extract(3), self.extract(2));
        let cross = scaling_imag * shuffled;
        parallel.addsub(cross)
    }

    #[inline]
    fn div_complex(self, value: f64x4) -> f64x4
    {
        let scaling_imag = f64x4::new(self.extract(0), self.extract(0), self.extract(2), self.extract(2));
        let scaling_real = f64x4::new(self.extract(1), self.extract(1), self.extract(3), self.extract(3));
        let parallel = scaling_real * value;
        // There should be a shufps operation which shuffles the vector self
        let shuffled = f64x4::new(value.extract(1), value.extract(0), value.extract(3), value.extract(2));
        let cross = scaling_imag * shuffled;
        let mul = parallel.addsub(cross);
        let square = shuffled * shuffled;
        let square_shuffled = f64x4::new(square.extract(1), square.extract(0), square.extract(3), square.extract(2));
        let sum = square + square_shuffled;
        let div = mul / sum;
        f64x4::new(div.extract(1), div.extract(0), div.extract(3), div.extract(2))
    }

    #[inline]
    fn complex_abs_squared(self) -> f64x4
    {
        let squared = self * self;
        squared.hadd(squared)
    }

    #[inline]
    fn complex_abs(self) -> f64x4
    {
        let squared_sum = self.complex_abs_squared();
        AvxF64x4::sqrt(squared_sum)
    }

    #[inline]
    fn complex_abs_squared2(self) -> f64x4
    {
        self.complex_abs_squared()
    }

    #[inline]
    fn complex_abs2(self) -> f64x4
    {
        self.complex_abs()
    }

    #[inline]
    fn sqrt(self) -> f64x4 {
        f64x4::new(self.extract(0).sqrt(), self.extract(1).sqrt(), self.extract(2).sqrt(), self.extract(3).sqrt())
    }

    #[inline]
    fn store_half_unchecked(self, target: &mut [f64], index: usize)
    {
        let mut temp = [0.0; 4];
        self.store(&mut temp, 0);
        unsafe {
            *target.get_unchecked_mut(index) = temp[0];
            *target.get_unchecked_mut(index + 1) = temp[2];
        }
    }

    #[inline]
    fn sum_real(&self) -> f64 {
        self.extract(0) +
        self.extract(1) +
        self.extract(2) +
        self.extract(3)
    }

    #[inline]
    fn sum_complex(&self) -> Complex<f64> {
        Complex::<f64>::new(self.extract(0) + self.extract(2), self.extract(1) + self.extract(3))
    }
}
<commit_msg>Fixed typo<commit_after>use num::complex::Complex;
use super::Simd;
use simd::x86::avx::{f32x8,f64x4,AvxF32x8,AvxF64x4};

pub type Reg32 = f32x8;

pub type Reg64 = f64x4;

impl Simd<f32> for f32x8
{
    type Array = [f32; 8];

    #[inline]
    fn to_array(self) -> Self::Array {
        let mut target = [0.0; 8];
        self.store(&mut target, 0);
        target
    }

    type ComplexArray = [Complex<f32>; 4];

    #[inline]
    fn len() -> usize {
        8
    }

    #[inline]
    fn load_wrap_unchecked(array: &[f32], idx: usize) -> f32x8 {
        let mut temp = [0.0; 8];
        for i in 0..temp.len() {
            temp[i] = unsafe {*array.get_unchecked((idx + i) % array.len())};
        }
        f32x8::load(&temp, 0)
    }

    #[inline]
    fn from_complex(value: Complex<f32>) -> f32x8 {
        f32x8::new(value.re, value.im, value.re, value.im,
                   value.re, value.im, value.re, value.im)
    }

    #[inline]
    fn add_real(self, value: f32) -> f32x8
    {
        let increment = f32x8::splat(value);
        self + increment
    }

    #[inline]
    fn add_complex(self, value: Complex<f32>) -> f32x8
    {
        let increment = f32x8::from_complex(value);
        self + increment
    }

    #[inline]
    fn scale_real(self, value: f32) -> f32x8
    {
        let scale_vector = f32x8::splat(value);
        self * scale_vector
    }

    #[inline]
    fn scale_complex(self, value: Complex<f32>) -> f32x8
    {
        let scaling_real = f32x8::splat(value.re);
        let scaling_imag = f32x8::splat(value.im);
        let parallel = scaling_real * self;
        // There should be a shufps operation which shuffles the vector self
        let shuffled = f32x8::new(self.extract(1), self.extract(0), self.extract(3), self.extract(2),
                                  self.extract(5), self.extract(4), self.extract(7), self.extract(6));
        let cross = scaling_imag * shuffled;
        parallel.addsub(cross)
    }

    #[inline]
    fn mul_complex(self, value: f32x8) -> f32x8
    {
        let scaling_real = f32x8::new(value.extract(0), value.extract(0), value.extract(2), value.extract(2),
                                      value.extract(4), value.extract(4), value.extract(6), value.extract(6));
        let scaling_imag = f32x8::new(value.extract(1), value.extract(1), value.extract(3), value.extract(3),
                                      value.extract(5), value.extract(5), value.extract(7), value.extract(7));
        let parallel = scaling_real * self;
        // There should be a shufps operation which shuffles the vector self
        let shuffled = f32x8::new(self.extract(1), self.extract(0), self.extract(3), self.extract(2),
                                  self.extract(5), self.extract(4), self.extract(7), self.extract(6));
        let cross = scaling_imag * shuffled;
        parallel.addsub(cross)
    }

    #[inline]
    fn div_complex(self, value: f32x8) -> f32x8
    {
                let scaling_imag = f32x8::new(self.extract(0), self.extract(0), self.extract(2), self.extract(2),
                                      self.extract(4), self.extract(4), self.extract(6), self.extract(6));
                let scaling_real = f32x8::new(self.extract(1), self.extract(1), self.extract(3), self.extract(3),
                                      self.extract(5), self.extract(5), self.extract(7), self.extract(7));
                let parallel = scaling_real * value;
                // There should be a shufps operation which shuffles the vector self
                let shuffled = f32x8::new(value.extract(1), value.extract(0), value.extract(3), value.extract(2),
                                  value.extract(5), value.extract(4), value.extract(7), value.extract(6));
                let cross = scaling_imag * shuffled;
                let mul = parallel.addsub(cross);
                let square = shuffled * shuffled;
                let square_shuffled = f32x8::new(square.extract(1), square.extract(0), square.extract(3), square.extract(2),
                                         square.extract(5), square.extract(4), square.extract(7), square.extract(6));
                let sum = square + square_shuffled;
                let div = mul / sum;
                f32x8::new(div.extract(1), div.extract(0), div.extract(3), div.extract(2),
                   div.extract(5), div.extract(4), div.extract(7), div.extract(6))
    }

    #[inline]
    fn complex_abs_squared(self) -> f32x8
    {
        let squared = self * self;
        squared.hadd(squared)
    }

    #[inline]
    fn complex_abs(self) -> f32x8
    {
        let squared_sum = self.complex_abs_squared();
        AvxF32x8::sqrt(squared_sum)
    }

    #[inline]
    fn complex_abs_squared2(self) -> f32x8
    {
        self.complex_abs_squared()
    }

    #[inline]
    fn complex_abs2(self) -> f32x8
    {
        self.complex_abs()
    }

    #[inline]
    fn sqrt(self) -> f32x8 {
        AvxF32x8::sqrt(self)
    }

    #[inline]
    fn store_half_unchecked(self, target: &mut [f32], index: usize)
    {
        unsafe {
            *target.get_unchecked_mut(index) = self.extract(0);
            *target.get_unchecked_mut(index+ 1) = self.extract(1);
            *target.get_unchecked_mut(index + 2) = self.extract(2);
            *target.get_unchecked_mut(index + 3) = self.extract(3);
        }
    }

    #[inline]
    fn sum_real(&self) -> f32 {
        self.extract(0) +
        self.extract(1) +
        self.extract(2) +
        self.extract(3) +
        self.extract(4) +
        self.extract(5) +
        self.extract(6) +
        self.extract(7)
    }

    #[inline]
    fn sum_complex(&self) -> Complex<f32> {
        Complex::<f32>::new(self.extract(0) + self.extract(2) + self.extract(4) + self.extract(6),
                            self.extract(1) + self.extract(3) + self.extract(5) + self.extract(7))
    }
}

impl Simd<f64> for f64x4
{
    type Array = [f64; 4];

    #[inline]
    fn to_array(self) -> Self::Array {
        let mut target = [0.0; 4];
        self.store(&mut target, 0);
        target
    }

    type ComplexArray = [Complex<f64>; 2];

    #[inline]
    fn len() -> usize {
        4
    }

    #[inline]
    fn load_wrap_unchecked(array: &[f64], idx: usize) -> f64x4 {
        let mut temp = [0.0; 4];
        for i in 0..temp.len() {
            temp[i] = unsafe { *array.get_unchecked((idx + i) % array.len()) };
        }
        f64x4::load(&temp, 0)
    }

    #[inline]
    fn from_complex(value: Complex<f64>) -> f64x4 {
        f64x4::new(value.re, value.im, value.re, value.im)
    }

    #[inline]
    fn add_real(self, value: f64) -> f64x4
    {
        let increment = f64x4::splat(value);
        self + increment
    }

    #[inline]
    fn add_complex(self, value: Complex<f64>) -> f64x4
    {
        let increment = f64x4::new(value.re, value.im, value.re, value.im);
        self + increment
    }

    #[inline]
    fn scale_real(self, value: f64) -> f64x4
    {
        let scale_vector = f64x4::splat(value);
        self * scale_vector
    }

    #[inline]
    fn scale_complex(self, value: Complex<f64>) -> f64x4
    {
        let scaling_real = f64x4::splat(value.re);
        let scaling_imag = f64x4::splat(value.im);
        let parallel = scaling_real * self;
        // There should be a shufps operation which shuffles the vector self
        let shuffled = f64x4::new(self.extract(1), self.extract(0), self.extract(3), self.extract(2));
        let cross = scaling_imag * shuffled;
        parallel.addsub(cross)
    }

    #[inline]
    fn mul_complex(self, value: f64x4) -> f64x4
    {
        let scaling_real = f64x4::new(value.extract(0), value.extract(0), value.extract(2), value.extract(2));
        let scaling_imag = f64x4::new(value.extract(1), value.extract(1), value.extract(3), value.extract(3));
        let parallel = scaling_real * self;
        // There should be a shufps operation which shuffles the vector self
        let shuffled = f64x4::new(self.extract(1), self.extract(0), self.extract(3), self.extract(2));
        let cross = scaling_imag * shuffled;
        parallel.addsub(cross)
    }

    #[inline]
    fn div_complex(self, value: f64x4) -> f64x4
    {
        let scaling_imag = f64x4::new(self.extract(0), self.extract(0), self.extract(2), self.extract(2));
        let scaling_real = f64x4::new(self.extract(1), self.extract(1), self.extract(3), self.extract(3));
        let parallel = scaling_real * value;
        // There should be a shufps operation which shuffles the vector self
        let shuffled = f64x4::new(value.extract(1), value.extract(0), value.extract(3), value.extract(2));
        let cross = scaling_imag * shuffled;
        let mul = parallel.addsub(cross);
        let square = shuffled * shuffled;
        let square_shuffled = f64x4::new(square.extract(1), square.extract(0), square.extract(3), square.extract(2));
        let sum = square + square_shuffled;
        let div = mul / sum;
        f64x4::new(div.extract(1), div.extract(0), div.extract(3), div.extract(2))
    }

    #[inline]
    fn complex_abs_squared(self) -> f64x4
    {
        let squared = self * self;
        squared.hadd(squared)
    }

    #[inline]
    fn complex_abs(self) -> f64x4
    {
        let squared_sum = self.complex_abs_squared();
        AvxF64x4::sqrt(squared_sum)
    }

    #[inline]
    fn complex_abs_squared2(self) -> f64x4
    {
        self.complex_abs_squared()
    }

    #[inline]
    fn complex_abs2(self) -> f64x4
    {
        self.complex_abs()
    }

    #[inline]
    fn sqrt(self) -> f64x4 {
        f64x4::new(self.extract(0).sqrt(), self.extract(1).sqrt(), self.extract(2).sqrt(), self.extract(3).sqrt())
    }

    #[inline]
    fn store_half_unchecked(self, target: &mut [f64], index: usize)
    {
        let mut temp = [0.0; 4];
        self.store(&mut temp, 0);
        unsafe {
            *target.get_unchecked_mut(index) = temp[0];
            *target.get_unchecked_mut(index + 1) = temp[2];
        }
    }

    #[inline]
    fn sum_real(&self) -> f64 {
        self.extract(0) +
        self.extract(1) +
        self.extract(2) +
        self.extract(3)
    }

    #[inline]
    fn sum_complex(&self) -> Complex<f64> {
        Complex::<f64>::new(self.extract(0) + self.extract(2), self.extract(1) + self.extract(3))
    }
}
<|endoftext|>