<commit_before>
use chrono;

// use std::cmp::Ord;
// use std::cmp::Ordering;
use hasher;

use serde_json;

use std::error::Error;
use std::collections::BTreeSet;
use std::collections::HashSet;
use self::chrono::offset::Utc;

type Address = String;
type Amount = i64;

#[derive(Debug)]
pub struct Blockchain {
    chain: BTreeSet<Block>,
    current_transactions: BTreeSet<Transaction>,
    nodes: HashSet<String>
}

#[derive(Debug)]
#[derive(Serialize)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct Transaction {
    sender: Address,
    recipient: Address,
    amount: Amount
}

#[derive(Debug)]
#[derive(Serialize)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct Block {
    index: usize,
    timestamp: i64,
    proof: i64,
    previous_hash: String,
    transactions: BTreeSet<Transaction>
}

impl Transaction {
    pub fn new(sender: Address, recipient: Address, amount: Amount) -> Transaction {
        Transaction {
            sender: sender,
            recipient: recipient,
            amount: amount
        }
    }
}

impl Blockchain {
    pub fn new() -> Blockchain {
        let mut blockchain = Blockchain {
            chain: BTreeSet::new(),
            current_transactions: BTreeSet::new(),
            nodes: HashSet::new()
        };
        //todo: how to do genesis?
        blockchain.new_block(100, String::from("some hash..."));
        blockchain
    }

    fn create_block(&self, proof: i64, previous_hash: String) -> Block {
        Block {
            index: self.chain.len() + 1,
            timestamp: Utc::now().timestamp(),
            proof: proof,
            previous_hash: previous_hash, //or self.hash(self.chain[-1]),
            transactions: BTreeSet::new()
        }
    }

    ///
    ///Create a new Block
    ///
    pub fn new_block(&mut self, proof: i64, previous_hash: String) -> &Block {

        let block = self.create_block(proof, previous_hash);

        self.current_transactions = BTreeSet::new();
        self.chain.insert(block);
        &self.chain.iter().next_back().expect("Just added element")
    }

    pub fn new_transaction(&mut self, sender: Address, recipient: Address, amount: Amount) -> usize {
        let txn = Transaction::new(sender, recipient, amount);
        self.current_transactions.insert(txn);
        self.last_block().index
    }

    pub fn last_block(&self) -> &Block {
        //for set, it's self.chain.iter().next_back()
        //it's a double-ended iterator, and it's sorted, so it should be fast
        self.chain.iter().next_back().expect("chain empty. expected genesis block")
    }

    pub fn hash(block: &Block) -> Result<String, String> {
       hasher::hash(block)
    }

    ///
    ///Simple Proof of Work Algorithm:
    ///          - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
    ///          - p is the previous proof, and p' is the new proof
    fn proof_of_work(&self, last_proof: u64) -> u64 {
        let mut proof = 0;
        while !Self::valid_proof(last_proof, proof) {
             proof += 1
        }
        println!("Took {} iterations",proof);
        return proof
    }

    /// Validates the Proof
    /// i.e. does the hash of last_proof and this proof end in 000?
    fn valid_proof(last_proof: u64, proof: u64) -> bool {

        let guess = format!("{}{}", last_proof,proof);
        let guess_hash =  hasher::hash_string(guess);
        //todo: make trace!
        //println!("guess_hash:{}", guess_hash);
        let is_valid = guess_hash.starts_with("000");
        if is_valid {
            debug!("guess_hash: {}", guess_hash);
        }
        is_valid
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashSet;
    use blockchain::Blockchain;

    #[test]
    fn new_transaction() {
        let mut blockchain = Blockchain::new();
        let idx = blockchain.new_transaction(String::from("a"), String::from("b"), 100);
        let last_txn = blockchain.current_transactions.iter().next_back().expect("expected a txn");
        assert_eq!(last_txn.sender, String::from("a"));
        assert_eq!(last_txn.recipient, String::from("b"));
        assert_eq!(last_txn.amount, 100);
    }

     #[test]
    fn new_block() {
        let mut blockchain = Blockchain::new();
        blockchain.new_transaction(String::from("a"), String::from("b"), 100);

        let a = blockchain.current_transactions.len();
        assert_eq!(1, a , "1 transaction");

        blockchain.new_block(2, String::from("abc"));

        let b = blockchain.current_transactions.len();
        assert_eq!(0, b, "New block should clear transactions (which were on the previous block");

    }

    #[test]
    fn hash() {
        let mut blockchain = Blockchain::new();
        blockchain.new_block(2, String::from("abc"));
        let block = blockchain.last_block();
        let hash = Blockchain::hash(block);
        let hash2 = Blockchain::hash(block);
        println!("{:?}", hash);
        assert!(hash.is_ok());
        assert_eq!(hash.unwrap(), hash2.unwrap(), "Expected same block to hash to the same value");
        //assert!(hash.unwrap().len() > 10, "expected a longer hash");
    }

    #[test]
    fn valid_proof_false() {
        let mut blockchain = Blockchain::new();
        assert_eq!(Blockchain::valid_proof(1,1), false);
    }

    #[test]
    fn proof_of_work() {
        let mut blockchain = Blockchain::new();
        let proof = blockchain.proof_of_work(1);
        println!("Proof of work: {}", proof);
        assert!(proof > 1, "expected a higher proof");
        assert!(Blockchain::valid_proof(1, proof));
    }

}

// import hashlib
// import json
// from time import time
// from urllib.parse import urlparse
// from uuid import uuid4

// import requests
// from flask import Flask, jsonify, request

// class Blockchain(object):
//     def __init__(self):
//         self.current_transactions = []
//         self.chain = []
//         self.nodes = set()

//         # Create the genesis block
//         self.new_block(previous_hash=1, proof=100)

//     def register_node(self, address):
//         """
//         Add a new node to the list of nodes

//         :param address: <str> Address of node. Eg. 'http://192.168.0.5:5000'
//         :return: None
//         """

//         parsed_url = urlparse(address)
//         self.nodes.add(parsed_url.netloc)

//     def valid_chain(self, chain):
//         """
//         Determine if a given blockchain is valid

//         :param chain: <list> A blockchain
//         :return: <bool> True if valid, False if not
//         """

//         last_block = chain[0]
//         current_index = 1

//         while current_index < len(chain):
//             block = chain[current_index]
//             print(f'{last_block}')
//             print(f'{block}')
//             print("\n-----------\n")
//             # Check that the hash of the block is correct
//             if block['previous_hash'] != self.hash(last_block):
//                 return False

//             # Check that the Proof of Work is correct
//             if not self.valid_proof(last_block['proof'], block['proof']):
//                 return False

//             last_block = block
//             current_index += 1

//         return True

//     def resolve_conflicts(self):
//         """
//         This is our consensus algorithm, it resolves conflicts
//         by replacing our chain with the longest one in the network.

//         :return: <bool> True if our chain was replaced, False if not
//         """

//         neighbours = self.nodes
//         new_chain = None

//         # We're only looking for chains longer than ours
//         max_length = len(self.chain)

//         # Grab and verify the chains from all the nodes in our network
//         for node in neighbours:
//             response = requests.get(f'http://{node}/chain')

//             if response.status_code == 200:
//                 length = response.json()['length']
//                 chain = response.json()['chain']

//                 # Check if the length is longer and the chain is valid
//                 if length > max_length and self.valid_chain(chain):
//                     max_length = length
//                     new_chain = chain

//         # Replace our chain if we discovered a new, valid chain longer than ours
//         if new_chain:
//             self.chain = new_chain
//             return True

//         return False

//     def new_block(self, proof, previous_hash=None):
//         """
//         Create a new Block in the Blockchain

//         :param proof: <int> The proof given by the Proof of Work algorithm
//         :param previous_hash: (Optional) <str> Hash of previous Block
//         :return: <dict> New Block
//         """

//         block = {
//             'index': len(self.chain) + 1,
//             'timestamp': time(),
//             'transactions': self.current_transactions,
//             'proof': proof,
//             'previous_hash': previous_hash or self.hash(self.chain[-1]),
//         }

//         # Reset the current list of transactions
//         self.current_transactions = []

//         self.chain.append(block)
//         return block

//     def new_transaction(self, sender, recipient, amount):
//         """
//         Creates a new transaction to go into the next mined Block

//         :param sender: <str> Address of the Sender
//         :param recipient: <str> Address of the Recipient
//         :param amount: <int> Amount
//         :return: <int> The index of the Block that will hold this transaction
//         """
//         self.current_transactions.append({
//             'sender': sender,
//             'recipient': recipient,
//             'amount': amount,
//         })

//         return self.last_block['index'] + 1

//     @property
//     def last_block(self):
//         return self.chain[-1]

//     @staticmethod
//     def hash(block):
//         """
//         Creates a SHA-256 hash of a Block

//         :param block: <dict> Block
//         :return: <str>
//         """

//         # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
//         block_string = json.dumps(block, sort_keys=True).encode()
//         return hashlib.sha256(block_string).hexdigest()

//     def proof_of_work(self, last_proof):
//         """
//         Simple Proof of Work Algorithm:
//          - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
//          - p is the previous proof, and p' is the new proof

//         :param last_proof: <int>
//         :return: <int>
//         """

//         proof = 0
//         while self.valid_proof(last_proof, proof) is False:
//             proof += 1

//         return proof

//     @staticmethod
//     def valid_proof(last_proof, proof):
//         """
//         Validates the Proof

//         :param last_proof: <int> Previous Proof
//         :param proof: <int> Current Proof
//         :return: <bool> True if correct, False if not.
//         """

//         guess = f'{last_proof}{proof}'.encode()
//         guess_hash = hashlib.sha256(guess).hexdigest()
//         return guess_hash[:4] == "0000"

// # Instantiate the Node
// app = Flask(__name__)

// # Generate a globally unique address for this node
// node_identifier = str(uuid4()).replace('-', '')

// # Instantiate the Blockchain
// blockchain = Blockchain()

// @app.route('/mine', methods=['GET'])
// def mine():
//     # We run the proof of work algorithm to get the next proof...
//     last_block = blockchain.last_block
//     last_proof = last_block['proof']
//     proof = blockchain.proof_of_work(last_proof)

//     # We must receive a reward for finding the proof.
//     # The sender is "0" to signify that this node has mined a new coin.
//     blockchain.new_transaction(
//         sender="0",
//         recipient=node_identifier,
//         amount=1,
//     )

//     # Forge the new Block by adding it to the chain
//     block = blockchain.new_block(proof)

//     response = {
//         'message': "New Block Forged",
//         'index': block['index'],
//         'transactions': block['transactions'],
//         'proof': block['proof'],
//         'previous_hash': block['previous_hash'],
//     }
//     return jsonify(response), 200

// @app.route('/transactions/new', methods=['POST'])
// def new_transaction():
//     values = request.get_json()

//     # Check that the required fields are in the POST'ed data
//     required = ['sender', 'recipient', 'amount']
//     if not all(k in values for k in required):
//         return 'Missing values', 400

//     # Create a new Transaction
//     index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])

//     response = {'message': f'Transaction will be added to Block {index}'}
//     return jsonify(response), 201

// @app.route('/chain', methods=['GET'])
// def full_chain():
//     response = {
//         'chain': blockchain.chain,
//         'length': len(blockchain.chain),
//     }
//     return jsonify(response), 200

// @app.route('/nodes/register', methods=['POST'])
// def register_nodes():
//     values = request.get_json()

//     nodes = values.get('nodes')
//     if nodes is None:
//         return "Error: Please supply a valid list of nodes", 400

//     for node in nodes:
//         blockchain.register_node(node)

//     response = {
//         'message': 'New nodes have been added',
//         'total_nodes': list(blockchain.nodes),
//     }
//     return jsonify(response), 201

// @app.route('/nodes/resolve', methods=['GET'])
// def consensus():
//     replaced = blockchain.resolve_conflicts()

//     if replaced:
//         response = {
//             'message': 'Our chain was replaced',
//             'new_chain': blockchain.chain
//         }
//     else:
//         response = {
//             'message': 'Our chain is authoritative',
//             'chain': blockchain.chain
//         }

//     return jsonify(response), 200

// if __name__ == '__main__':
//     app.run(host='0.0.0.0', port=5000)<commit_msg>start of mining<commit_after>
use chrono;

// use std::cmp::Ord;
// use std::cmp::Ordering;
use hasher;

use serde_json;

use std::error::Error;
use std::collections::BTreeSet;
use std::collections::HashSet;
use self::chrono::offset::Utc;

type Address = String;
type Amount = i64;

#[derive(Debug)]
pub struct Blockchain {
    chain: BTreeSet<Block>,
    current_transactions: BTreeSet<Transaction>,
    nodes: HashSet<String>
}

#[derive(Debug)]
#[derive(Serialize)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct Transaction {
    sender: Address,
    recipient: Address,
    amount: Amount
}

#[derive(Debug)]
#[derive(Serialize)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct Block {
    index: usize,
    timestamp: i64,
    proof: u64,
    previous_hash: String,
    transactions: BTreeSet<Transaction>
}

impl Transaction {
    pub fn new(sender: Address, recipient: Address, amount: Amount) -> Transaction {
        Transaction {
            sender: sender,
            recipient: recipient,
            amount: amount
        }
    }
}

impl Blockchain {
    pub fn new() -> Blockchain {
        let mut blockchain = Blockchain {
            chain: BTreeSet::new(),
            current_transactions: BTreeSet::new(),
            nodes: HashSet::new()
        };
        //todo: how to do genesis?
        blockchain.new_block(100, String::from("some hash..."));
        blockchain
    }

    fn create_block(&self, proof: u64, previous_hash: String) -> Block {
        Block {
            index: self.chain.len() + 1,
            timestamp: Utc::now().timestamp(),
            proof: proof,
            previous_hash: previous_hash, //or self.hash(self.chain[-1]),
            transactions: self.current_transactions
        }
    }

    ///
    ///Create a new Block
    ///
    pub fn new_block(&mut self, proof: u64, previous_hash: String) -> &Block {

        let block = self.create_block(proof, previous_hash);

        self.current_transactions = BTreeSet::new();
        self.chain.insert(block);
        &self.chain.iter().next_back().expect("Just added element")
    }

    pub fn new_transaction(&mut self, sender: Address, recipient: Address, amount: Amount) -> usize {
        let txn = Transaction::new(sender, recipient, amount);
        self.current_transactions.insert(txn);
        self.last_block().index
    }

    pub fn last_block(&self) -> &Block {
        //for set, it's self.chain.iter().next_back()
        //it's a double-ended iterator, and it's sorted, so it should be fast
        self.chain.iter().next_back().expect("chain empty. expected genesis block")
    }

    pub fn hash(block: &Block) -> Result<String, String> {
       hasher::hash(block)
    }

    ///
    ///Simple Proof of Work Algorithm:
    ///          - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
    ///          - p is the previous proof, and p' is the new proof
    fn proof_of_work(last_proof: u64) -> u64 {
        let mut proof = 0;
        while !Self::valid_proof(last_proof, proof) {
             proof += 1
        }
        println!("Took {} iterations",proof);
        return proof
    }

    /// Validates the Proof
    /// i.e. does the hash of last_proof and this proof end in 000?
    fn valid_proof(last_proof: u64, proof: u64) -> bool {

        let guess = format!("{}{}", last_proof,proof);
        let guess_hash =  hasher::hash_string(guess);
        //todo: make trace!
        //println!("guess_hash:{}", guess_hash);
        let is_valid = guess_hash.starts_with("000");
        if is_valid {
            debug!("guess_hash: {}", guess_hash);
        }
        is_valid
    }

    fn work(&self) -> u64{
        let last_block = self.last_block();
        let last_proof = last_block.proof;
        //Mine!
        Self::proof_of_work(last_proof)
    }

    fn hash_last_block(&self) -> String {
        let last_block = self.last_block();
        Self::hash(last_block).expect("hash block failed")
    }

    fn mine(&mut self) -> &Block {
        // We run the proof of work algorithm to get the next proof...
        let new_block_proof = self.work();
        //Got it. Give ourselves the new coin (block?)
        //The sender is "0" to signify that this node has mined a new coin.
        self.new_transaction(String::from("0"), String::from("todo: node identifier"), 1);
        let previous_hash = self.hash_last_block();
        //Forge the new Block by adding it to the chain
        let mined_block = self.new_block(new_block_proof, previous_hash);
        &mined_block
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashSet;
    use blockchain::Blockchain;

    #[test]
    fn new_transaction() {
        let mut blockchain = Blockchain::new();
        let idx = blockchain.new_transaction(String::from("a"), String::from("b"), 100);
        let last_txn = blockchain.current_transactions.iter().next_back().expect("expected a txn");
        assert_eq!(last_txn.sender, String::from("a"));
        assert_eq!(last_txn.recipient, String::from("b"));
        assert_eq!(last_txn.amount, 100);
    }

     #[test]
    fn new_block() {
        let mut blockchain = Blockchain::new();
        blockchain.new_transaction(String::from("a"), String::from("b"), 100);

        let a = blockchain.current_transactions.len();
        assert_eq!(1, a , "1 transaction");

        blockchain.new_block(2, String::from("abc"));

        let b = blockchain.current_transactions.len();
        assert_eq!(0, b, "New block should clear transactions (which were on the previous block");

    }

    #[test]
    fn hash() {
        let mut blockchain = Blockchain::new();
        blockchain.new_block(2, String::from("abc"));
        let block = blockchain.last_block();
        let hash = Blockchain::hash(block);
        let hash2 = Blockchain::hash(block);
        println!("{:?}", hash);
        assert!(hash.is_ok());
        assert_eq!(hash.unwrap(), hash2.unwrap(), "Expected same block to hash to the same value");
        //assert!(hash.unwrap().len() > 10, "expected a longer hash");
    }

    #[test]
    fn valid_proof_false() {
        let mut blockchain = Blockchain::new();
        assert_eq!(Blockchain::valid_proof(1,1), false);
    }

    #[test]
    fn proof_of_work() {
        let mut blockchain = Blockchain::new();
        let proof = blockchain.proof_of_work(1);
        println!("Proof of work: {}", proof);
        assert!(proof > 1, "expected a higher proof");
        assert!(Blockchain::valid_proof(1, proof));
    }

}

// import hashlib
// import json
// from time import time
// from urllib.parse import urlparse
// from uuid import uuid4

// import requests
// from flask import Flask, jsonify, request

// class Blockchain(object):
//     def __init__(self):
//         self.current_transactions = []
//         self.chain = []
//         self.nodes = set()

//         # Create the genesis block
//         self.new_block(previous_hash=1, proof=100)

//     def register_node(self, address):
//         """
//         Add a new node to the list of nodes

//         :param address: <str> Address of node. Eg. 'http://192.168.0.5:5000'
//         :return: None
//         """

//         parsed_url = urlparse(address)
//         self.nodes.add(parsed_url.netloc)

//     def valid_chain(self, chain):
//         """
//         Determine if a given blockchain is valid

//         :param chain: <list> A blockchain
//         :return: <bool> True if valid, False if not
//         """

//         last_block = chain[0]
//         current_index = 1

//         while current_index < len(chain):
//             block = chain[current_index]
//             print(f'{last_block}')
//             print(f'{block}')
//             print("\n-----------\n")
//             # Check that the hash of the block is correct
//             if block['previous_hash'] != self.hash(last_block):
//                 return False

//             # Check that the Proof of Work is correct
//             if not self.valid_proof(last_block['proof'], block['proof']):
//                 return False

//             last_block = block
//             current_index += 1

//         return True

//     def resolve_conflicts(self):
//         """
//         This is our consensus algorithm, it resolves conflicts
//         by replacing our chain with the longest one in the network.

//         :return: <bool> True if our chain was replaced, False if not
//         """

//         neighbours = self.nodes
//         new_chain = None

//         # We're only looking for chains longer than ours
//         max_length = len(self.chain)

//         # Grab and verify the chains from all the nodes in our network
//         for node in neighbours:
//             response = requests.get(f'http://{node}/chain')

//             if response.status_code == 200:
//                 length = response.json()['length']
//                 chain = response.json()['chain']

//                 # Check if the length is longer and the chain is valid
//                 if length > max_length and self.valid_chain(chain):
//                     max_length = length
//                     new_chain = chain

//         # Replace our chain if we discovered a new, valid chain longer than ours
//         if new_chain:
//             self.chain = new_chain
//             return True

//         return False

//     def new_block(self, proof, previous_hash=None):
//         """
//         Create a new Block in the Blockchain

//         :param proof: <int> The proof given by the Proof of Work algorithm
//         :param previous_hash: (Optional) <str> Hash of previous Block
//         :return: <dict> New Block
//         """

//         block = {
//             'index': len(self.chain) + 1,
//             'timestamp': time(),
//             'transactions': self.current_transactions,
//             'proof': proof,
//             'previous_hash': previous_hash or self.hash(self.chain[-1]),
//         }

//         # Reset the current list of transactions
//         self.current_transactions = []

//         self.chain.append(block)
//         return block

//     def new_transaction(self, sender, recipient, amount):
//         """
//         Creates a new transaction to go into the next mined Block

//         :param sender: <str> Address of the Sender
//         :param recipient: <str> Address of the Recipient
//         :param amount: <int> Amount
//         :return: <int> The index of the Block that will hold this transaction
//         """
//         self.current_transactions.append({
//             'sender': sender,
//             'recipient': recipient,
//             'amount': amount,
//         })

//         return self.last_block['index'] + 1

//     @property
//     def last_block(self):
//         return self.chain[-1]

//     @staticmethod
//     def hash(block):
//         """
//         Creates a SHA-256 hash of a Block

//         :param block: <dict> Block
//         :return: <str>
//         """

//         # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
//         block_string = json.dumps(block, sort_keys=True).encode()
//         return hashlib.sha256(block_string).hexdigest()

//     def proof_of_work(self, last_proof):
//         """
//         Simple Proof of Work Algorithm:
//          - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
//          - p is the previous proof, and p' is the new proof

//         :param last_proof: <int>
//         :return: <int>
//         """

//         proof = 0
//         while self.valid_proof(last_proof, proof) is False:
//             proof += 1

//         return proof

//     @staticmethod
//     def valid_proof(last_proof, proof):
//         """
//         Validates the Proof

//         :param last_proof: <int> Previous Proof
//         :param proof: <int> Current Proof
//         :return: <bool> True if correct, False if not.
//         """

//         guess = f'{last_proof}{proof}'.encode()
//         guess_hash = hashlib.sha256(guess).hexdigest()
//         return guess_hash[:4] == "0000"

// # Instantiate the Node
// app = Flask(__name__)

// # Generate a globally unique address for this node
// node_identifier = str(uuid4()).replace('-', '')

// # Instantiate the Blockchain
// blockchain = Blockchain()

// @app.route('/mine', methods=['GET'])
// def mine():
//     # We run the proof of work algorithm to get the next proof...
//     last_block = blockchain.last_block
//     last_proof = last_block['proof']
//     proof = blockchain.proof_of_work(last_proof)

//     # We must receive a reward for finding the proof.
//     # The sender is "0" to signify that this node has mined a new coin.
//     blockchain.new_transaction(
//         sender="0",
//         recipient=node_identifier,
//         amount=1,
//     )

//     # Forge the new Block by adding it to the chain
//     block = blockchain.new_block(proof)

//     response = {
//         'message': "New Block Forged",
//         'index': block['index'],
//         'transactions': block['transactions'],
//         'proof': block['proof'],
//         'previous_hash': block['previous_hash'],
//     }
//     return jsonify(response), 200

// @app.route('/transactions/new', methods=['POST'])
// def new_transaction():
//     values = request.get_json()

//     # Check that the required fields are in the POST'ed data
//     required = ['sender', 'recipient', 'amount']
//     if not all(k in values for k in required):
//         return 'Missing values', 400

//     # Create a new Transaction
//     index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])

//     response = {'message': f'Transaction will be added to Block {index}'}
//     return jsonify(response), 201

// @app.route('/chain', methods=['GET'])
// def full_chain():
//     response = {
//         'chain': blockchain.chain,
//         'length': len(blockchain.chain),
//     }
//     return jsonify(response), 200

// @app.route('/nodes/register', methods=['POST'])
// def register_nodes():
//     values = request.get_json()

//     nodes = values.get('nodes')
//     if nodes is None:
//         return "Error: Please supply a valid list of nodes", 400

//     for node in nodes:
//         blockchain.register_node(node)

//     response = {
//         'message': 'New nodes have been added',
//         'total_nodes': list(blockchain.nodes),
//     }
//     return jsonify(response), 201

// @app.route('/nodes/resolve', methods=['GET'])
// def consensus():
//     replaced = blockchain.resolve_conflicts()

//     if replaced:
//         response = {
//             'message': 'Our chain was replaced',
//             'new_chain': blockchain.chain
//         }
//     else:
//         response = {
//             'message': 'Our chain is authoritative',
//             'chain': blockchain.chain
//         }

//     return jsonify(response), 200

// if __name__ == '__main__':
//     app.run(host='0.0.0.0', port=5000)<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: GPL-2.0
//

const VERSION: &str = env!("VERSION");

pub fn version() -> &'static str {
    VERSION
}
<commit_msg>build_info: Add documentation<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: GPL-2.0
//

//! Build information module

const VERSION: &str = env!("VERSION");

/// Returns the version in use, including the commit and if there is
/// uncommited modification in the source.
///
/// Internally, it use `git describe` to get the version and the
/// number of changes since the last tag.
///
/// # Example
/// ```
/// use updatehub::build_info;
///
/// println!("Running version: {}", build_info::version());
/// ```
pub fn version() -> &'static str {
    VERSION
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

/// This client's writer channel
static mut writer_tx: *mut Sender<Vec<u8>> = 0 as *mut Sender<Vec<u8>>;

/// This client's kill channel
static mut kill_tx: *mut Sender<()> = 0 as *mut Sender<()>;

#[no_mangle]
pub extern "C" fn hydrogen_start(address: *const c_char,
    data_handler: extern fn(*const c_char),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    // TODO - adjust this to accept a log level adjustable by whoever is running
    // the application
    super::init();

    trace!("Rust - start()");

    let mut r_address;
    unsafe {
        r_address = CStr::from_ptr(address);
    }
    let s_address = r_address.to_bytes();
    let host_address = match str::from_utf8(s_address) {
        Ok(safe_str) => safe_str,
        Err(_) => {
            error!("Invalid host address");
            return -1 as c_int;
        }
    };
    trace!("Address: ");

    // Create and register a way to kill this client
    let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    unsafe { kill_tx = &mut k_tx.clone(); }

    // // Writer thread's channel
    // let (w_tx, writer_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    // unsafe { *writer_tx = w_tx.clone(); }
    //
    // debug!("Attempting connect to: {}", host_address);
    //
    // let result = TcpStream::connect(host_address);
    // if result.is_err() {
    //     error!("Error connecting to {} - {}", host_address, result.unwrap_err());
    //     return -1 as c_int;
    // }
    // debug!("Connected");
    // on_connect_handler();
    //
    // let stream = result.unwrap();
    // let client = Bstream::new(stream);
    // let r_client = client.clone();
    // let w_client = client.clone();
    //
    // // Start the reader thread
    // thread::Builder::new()
    //     .name("ReaderThread".to_string())
    //     .spawn(move||{
    //         reader_thread(r_client, data_handler)
    //     }).unwrap();
    //
    // // Start the writer thread
    // thread::Builder::new()
    //     .name("WriterThread".to_string())
    //     .spawn(move||{
    //         writer_thread(writer_rx, w_client)
    //     }).unwrap();
    //
    // // Wait for the kill signal
    // match kill_rx.recv() {
    //     Ok(_) => { }
    //     Err(e) => {
    //         error!("Error on kill channel: {}", e);
    //         on_disconnect_handler();
    //         return -1 as c_int;
    //     }
    // };
    // on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn hydrogen_write(buffer: *const c_char) -> c_int {
    trace!("Rust.write");

    let mut buf_as_cstr;
    unsafe {
        buf_as_cstr = CStr::from_ptr(buffer);
    }
    let buf_as_slice = buf_as_cstr.to_bytes();

    let mut n_buffer = Vec::<u8>::with_capacity(buf_as_slice.len());
    for byte in buf_as_slice.iter() {
        n_buffer.push(*byte);
    }

    unsafe {
        match (*writer_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                warn!("Error sending buffer: {}", e);
                let _ = (*kill_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream, handler: extern fn(*const c_char)) {
    trace!("Rust.reader_thread started");

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        handler(c_buffer.as_ptr());
                    }).unwrap();
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }
    debug!("Rust.reader_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream) {
    trace!("Rust.writer_thread started");

    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        error!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }

    debug!("Rust.writer_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Drops the current connection and kills all current threads
#[no_mangle]
pub extern "C" fn hydrogen_kill() {
    unsafe { let _ = (*kill_tx).send(()); }
}
<commit_msg>Debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

/// This client's writer channel
static mut writer_tx: *mut Sender<Vec<u8>> = 0 as *mut Sender<Vec<u8>>;

/// This client's kill channel
static mut kill_tx: *mut Sender<()> = 0 as *mut Sender<()>;

#[no_mangle]
pub extern "C" fn hydrogen_start(address: *const c_char,
    data_handler: extern fn(*const c_char),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    // TODO - adjust this to accept a log level adjustable by whoever is running
    // the application
    super::init();

    trace!("Rust - start()");

    let mut r_address;
    unsafe {
        r_address = CStr::from_ptr(address);
    }
    let s_address = r_address.to_bytes();
    let host_address = match str::from_utf8(s_address) {
        Ok(safe_str) => safe_str,
        Err(_) => {
            error!("Invalid host address");
            return -1 as c_int;
        }
    };
    trace!("Address: ");

    // Create and register a way to kill this client
    let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    unsafe { kill_tx = &mut k_tx.clone(); }

    // Writer thread's channel
    let (w_tx, writer_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    unsafe { writer_tx = &mut w_tx.clone(); }

    debug!("Attempting connect to: {}", host_address);

    let result = TcpStream::connect(host_address);
    if result.is_err() {
        error!("Error connecting to {} - {}", host_address, result.unwrap_err());
        return -1 as c_int;
    }
    debug!("Connected");
    on_connect_handler();

    let stream = result.unwrap();
    let client = Bstream::new(stream);
    let r_client = client.clone();
    let w_client = client.clone();

    // Start the reader thread
    thread::Builder::new()
        .name("ReaderThread".to_string())
        .spawn(move||{
            reader_thread(r_client, data_handler)
        }).unwrap();

    // Start the writer thread
    thread::Builder::new()
        .name("WriterThread".to_string())
        .spawn(move||{
            writer_thread(writer_rx, w_client)
        }).unwrap();

    // Wait for the kill signal
    match kill_rx.recv() {
        Ok(_) => { }
        Err(e) => {
            error!("Error on kill channel: {}", e);
            on_disconnect_handler();
            return -1 as c_int;
        }
    };
    on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn hydrogen_write(buffer: *const c_char) -> c_int {
    trace!("Rust.write");

    let mut buf_as_cstr;
    unsafe {
        buf_as_cstr = CStr::from_ptr(buffer);
    }
    let buf_as_slice = buf_as_cstr.to_bytes();

    let mut n_buffer = Vec::<u8>::with_capacity(buf_as_slice.len());
    for byte in buf_as_slice.iter() {
        n_buffer.push(*byte);
    }

    unsafe {
        match (*writer_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                warn!("Error sending buffer: {}", e);
                let _ = (*kill_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream, handler: extern fn(*const c_char)) {
    trace!("Rust.reader_thread started");

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        handler(c_buffer.as_ptr());
                    }).unwrap();
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }
    debug!("Rust.reader_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream) {
    trace!("Rust.writer_thread started");

    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        error!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }

    debug!("Rust.writer_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Drops the current connection and kills all current threads
#[no_mangle]
pub extern "C" fn hydrogen_kill() {
    unsafe { let _ = (*kill_tx).send(()); }
}
<|endoftext|>


<commit_before>//! Implements higher-level facilities.

pub mod time_manager;

use std::collections::VecDeque;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use chesstypes::*;
use tt::*;
use uci::*;
use search::*;
use board::*;
use board::rules::Position;
use self::time_manager::*;

/// The version of the program.
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";

/// 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<S, E>
    where S: SearchExecutor,
          E: BoardEvaluator
{
    tt: Arc<Tt>,
    position: Position<E>,
    current_depth: u8,

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

    // 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 of play to calculate and
    // send to the GUI (the first move in each line will be
    // 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<EngineReply>,
}

impl<S: SearchExecutor, E: BoardEvaluator + 'static> Engine<S, E> {
    /// A helper method. It it adds a progress report message to the
    /// queue.
    fn queue_progress_report(&mut self) {
        let &SearchStatus { depth, searched_nodes, nps, .. } = self.search_thread.status();
        self.queue.push_back(EngineReply::Info(vec![
            InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
            InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
            InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
        ]));
        self.silent_since = SystemTime::now();
    }

    /// A helper method. It it adds a message containing the current
    /// (multi)PV to the 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(EngineReply::Info(vec![
                InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
                InfoItem { info_type: "multipv".to_string(), data: format!("{}", i + 1) },
                InfoItem { info_type: "score".to_string(), data: format!("cp {}{}", value, bound_suffix) },
                InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
                InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
                InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
                InfoItem { info_type: "pv".to_string(), data: moves_string },
            ]));
        }
        self.silent_since = SystemTime::now();
    }

    /// A helper method. It it adds a message containing the current
    /// best move to the queue.
    fn queue_best_move(&mut self) {
        let &SearchStatus { ref variations, .. } = self.search_thread.status();
        self.queue.push_back(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<S: SearchExecutor, E: BoardEvaluator + 'static> SetOption for Engine<S, E> {
    fn options() -> Vec<(String, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
            ("MultiPV".to_string(), OptionDescription::Spin { min: 1, max: 500, default: 1 }),
        ]
    }

    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.
            _ => (),
        }
    }
}

impl<S: SearchExecutor, E: BoardEvaluator + 'static> UciEngine for Engine<S, E> {
    fn name() -> String {
        format!("{} {}", NAME, VERSION)
    }

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

    fn new(tt_size_mb: Option<usize>) -> Engine<S, E> {
        let mut tt = Tt::new();
        if let Some(tt_size_mb) = tt_size_mb {
            tt.resize(tt_size_mb);
        }
        let tt = Arc::new(tt);
        Engine {
            tt: tt.clone(),
            position: Position::from_fen(START_POSITION_FEN).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(),
        }
    }

    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;
        }
    }

    fn go(&mut self, params: GoParams) {
        // Note: We ignore the "mate" field.
        let GoParams { searchmoves,
                       ponder,
                       wtime,
                       btime,
                       winc,
                       binc,
                       movestogo,
                       depth,
                       nodes,
                       movetime,
                       infinite,
                       .. } = params;

        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 wait_for_reply(&mut self, duration: Duration) -> Option<EngineReply> {
        if self.queue.is_empty() {
            let done = self.search_thread.status().done;

            // Wait for `search_thread` to do some work, and hopefully
            // update its status. (We must do this even when the
            // search is done -- in that case the next line will just
            // yield the CPU to another process.)
            self.search_thread.wait_status_update(duration);

            if !done {
                let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } =
                    self.search_thread.status();

                // Send the (multi)PV for each newly reached depth.
                if depth > self.current_depth {
                    self.current_depth = depth;
                    self.queue_pv();
                }

                // Send a progress report periodically.
                if self.silent_since.elapsed().unwrap().as_secs() > 10 {
                    self.queue_progress_report();
                }

                // Register the 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.stop();
    }
}

/// A thread that executes consecutive searches in different starting
/// positions.
struct SearchThread<S: SearchExecutor> {
    tt: Arc<Tt>,
    position: Box<SearchNode>,
    status: SearchStatus,
    searcher: S,
}

impl<S: SearchExecutor> SearchThread<S> {
    /// Creates a new instance.
    pub fn new(tt: Arc<Tt>) -> SearchThread<S> {
        use board::evaluators::RandomEvaluator;
        SearchThread {
            tt: tt.clone(),
            position: Box::new(Position::<RandomEvaluator>::from_fen(START_POSITION_FEN)
                                   .ok()
                                   .unwrap()),
            status: SearchStatus {
                done: true,
                depth: 0,
                variations: vec![Variation {
                                     value: VALUE_MIN,
                                     bound: BOUND_LOWER,
                                     moves: vec![],
                                 }],
                searchmoves_count: 20,
                searched_nodes: 0,
                started_at: SystemTime::now(),
                duration_millis: 0,
                nps: 0,
            },
            searcher: S::new(tt),
        }
    }

    /// Terminates the currently running search (if any), starts a new
    /// search, updates the status.
    ///
    /// `position` is the starting position for the new
    /// search. `variation_count` specifies how many best lines of
    /// play to calculate (the first move in each line will be
    /// different). `searchmoves` restricts the analysis to the
    /// supplied list of moves only (no restrictions if the suppied
    /// list is empty). The move format is long algebraic
    /// notation. Examples: e2e4, e7e5, e1g1 (white short castling),
    /// e7e8q (for promotion).
    pub fn search(&mut self,
                  position: &SearchNode,
                  variation_count: usize,
                  mut searchmoves: Vec<String>) {
        self.position = position.copy();

        // Validate `searchmoves`.
        let mut moves = vec![];
        let legal_moves = position.legal_moves();
        if !searchmoves.is_empty() {
            searchmoves.sort();
            for m in legal_moves.iter() {
                if searchmoves.binary_search(&m.notation()).is_ok() {
                    moves.push(*m);
                }
            }
        };
        let searchmoves = if moves.is_empty() {
            legal_moves
        } else {
            moves
        };

        // Terminate the currently running search.
        self.stop();

        // Update the status.
        self.status = SearchStatus {
            done: false,
            depth: 0,
            searchmoves_count: searchmoves.len(),
            variations: vec![Variation {
                                 value: VALUE_MIN,
                                 bound: BOUND_LOWER,
                                 moves: searchmoves.first().map_or(vec![], |m| vec![*m]),
                             }],
            searched_nodes: 0,
            started_at: SystemTime::now(),
            duration_millis: 0,
            ..self.status
        };

        // Start a new search.
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: position.copy(),
            depth: DEPTH_MAX,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: searchmoves,
            variation_count: variation_count,
        });
    }

    /// Terminates the currently running search (if any), updates the
    /// status.
    pub fn stop(&mut self) {
        self.searcher.terminate_search();
        while !self.status().done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    /// Returns the status of the current search.
    ///
    /// **Important note:** Consecutive calls to this method will
    /// return the same unchanged result. Only after calling `search`,
    /// `stop`, or `wait_status_update`, the result returned by
    /// `status` may change.
    #[inline(always)]
    pub fn status(&self) -> &SearchStatus {
        &self.status
    }

    /// Waits for a search status update, timing out after a specified
    /// duration or earlier.
    pub fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    /// A helper method. It updates the current status according to
    /// the received report message.
    fn process_report(&mut self, report: SearchReport) {
        let duration = self.status.started_at.elapsed().unwrap();
        self.status.duration_millis = 1000 * duration.as_secs() +
                                      (duration.subsec_nanos() / 1000000) as u64;
        self.status.searched_nodes = report.searched_nodes;
        self.status.nps = 1000 * (self.status.nps + self.status.searched_nodes) /
                          (1000 + self.status.duration_millis);
        if self.status.depth < report.depth {
            self.status.depth = report.depth;
            self.status.variations = vec![extract_pv(&self.tt,
                                                     self.position.as_ref(),
                                                     report.depth)];
        }
        self.status.done = report.done;
    }
}
<commit_msg>make defalut hash 16<commit_after>//! Implements higher-level facilities.

pub mod time_manager;

use std::collections::VecDeque;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use chesstypes::*;
use tt::*;
use uci::*;
use search::*;
use board::*;
use board::rules::Position;
use self::time_manager::*;

/// The version of the program.
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";

/// 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<S, E>
    where S: SearchExecutor,
          E: BoardEvaluator
{
    tt: Arc<Tt>,
    position: Position<E>,
    current_depth: u8,

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

    // 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 of play to calculate and
    // send to the GUI (the first move in each line will be
    // 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<EngineReply>,
}

impl<S: SearchExecutor, E: BoardEvaluator + 'static> Engine<S, E> {
    /// A helper method. It it adds a progress report message to the
    /// queue.
    fn queue_progress_report(&mut self) {
        let &SearchStatus { depth, searched_nodes, nps, .. } = self.search_thread.status();
        self.queue.push_back(EngineReply::Info(vec![
            InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
            InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
            InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
        ]));
        self.silent_since = SystemTime::now();
    }

    /// A helper method. It it adds a message containing the current
    /// (multi)PV to the 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(EngineReply::Info(vec![
                InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
                InfoItem { info_type: "multipv".to_string(), data: format!("{}", i + 1) },
                InfoItem { info_type: "score".to_string(), data: format!("cp {}{}", value, bound_suffix) },
                InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
                InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
                InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
                InfoItem { info_type: "pv".to_string(), data: moves_string },
            ]));
        }
        self.silent_since = SystemTime::now();
    }

    /// A helper method. It it adds a message containing the current
    /// best move to the queue.
    fn queue_best_move(&mut self) {
        let &SearchStatus { ref variations, .. } = self.search_thread.status();
        self.queue.push_back(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<S: SearchExecutor, E: BoardEvaluator + 'static> SetOption for Engine<S, E> {
    fn options() -> Vec<(String, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
            ("MultiPV".to_string(), OptionDescription::Spin { min: 1, max: 500, default: 1 }),
        ]
    }

    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.
            _ => (),
        }
    }
}

impl<S: SearchExecutor, E: BoardEvaluator + 'static> UciEngine for Engine<S, E> {
    fn name() -> String {
        format!("{} {}", NAME, VERSION)
    }

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

    fn new(tt_size_mb: Option<usize>) -> Engine<S, E> {
        let mut tt = Tt::new();
        let tt_size_mb = tt_size_mb.unwrap_or(16);
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        Engine {
            tt: tt.clone(),
            position: Position::from_fen(START_POSITION_FEN).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(),
        }
    }

    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;
        }
    }

    fn go(&mut self, params: GoParams) {
        // Note: We ignore the "mate" field.
        let GoParams { searchmoves,
                       ponder,
                       wtime,
                       btime,
                       winc,
                       binc,
                       movestogo,
                       depth,
                       nodes,
                       movetime,
                       infinite,
                       .. } = params;

        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 wait_for_reply(&mut self, duration: Duration) -> Option<EngineReply> {
        if self.queue.is_empty() {
            let done = self.search_thread.status().done;

            // Wait for `search_thread` to do some work, and hopefully
            // update its status. (We must do this even when the
            // search is done -- in that case the next line will just
            // yield the CPU to another process.)
            self.search_thread.wait_status_update(duration);

            if !done {
                let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } =
                    self.search_thread.status();

                // Send the (multi)PV for each newly reached depth.
                if depth > self.current_depth {
                    self.current_depth = depth;
                    self.queue_pv();
                }

                // Send a progress report periodically.
                if self.silent_since.elapsed().unwrap().as_secs() > 10 {
                    self.queue_progress_report();
                }

                // Register the 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.stop();
    }
}

/// A thread that executes consecutive searches in different starting
/// positions.
struct SearchThread<S: SearchExecutor> {
    tt: Arc<Tt>,
    position: Box<SearchNode>,
    status: SearchStatus,
    searcher: S,
}

impl<S: SearchExecutor> SearchThread<S> {
    /// Creates a new instance.
    pub fn new(tt: Arc<Tt>) -> SearchThread<S> {
        use board::evaluators::RandomEvaluator;
        SearchThread {
            tt: tt.clone(),
            position: Box::new(Position::<RandomEvaluator>::from_fen(START_POSITION_FEN)
                                   .ok()
                                   .unwrap()),
            status: SearchStatus {
                done: true,
                depth: 0,
                variations: vec![Variation {
                                     value: VALUE_MIN,
                                     bound: BOUND_LOWER,
                                     moves: vec![],
                                 }],
                searchmoves_count: 20,
                searched_nodes: 0,
                started_at: SystemTime::now(),
                duration_millis: 0,
                nps: 0,
            },
            searcher: S::new(tt),
        }
    }

    /// Terminates the currently running search (if any), starts a new
    /// search, updates the status.
    ///
    /// `position` is the starting position for the new
    /// search. `variation_count` specifies how many best lines of
    /// play to calculate (the first move in each line will be
    /// different). `searchmoves` restricts the analysis to the
    /// supplied list of moves only (no restrictions if the suppied
    /// list is empty). The move format is long algebraic
    /// notation. Examples: e2e4, e7e5, e1g1 (white short castling),
    /// e7e8q (for promotion).
    pub fn search(&mut self,
                  position: &SearchNode,
                  variation_count: usize,
                  mut searchmoves: Vec<String>) {
        self.position = position.copy();

        // Validate `searchmoves`.
        let mut moves = vec![];
        let legal_moves = position.legal_moves();
        if !searchmoves.is_empty() {
            searchmoves.sort();
            for m in legal_moves.iter() {
                if searchmoves.binary_search(&m.notation()).is_ok() {
                    moves.push(*m);
                }
            }
        };
        let searchmoves = if moves.is_empty() {
            legal_moves
        } else {
            moves
        };

        // Terminate the currently running search.
        self.stop();

        // Update the status.
        self.status = SearchStatus {
            done: false,
            depth: 0,
            searchmoves_count: searchmoves.len(),
            variations: vec![Variation {
                                 value: VALUE_MIN,
                                 bound: BOUND_LOWER,
                                 moves: searchmoves.first().map_or(vec![], |m| vec![*m]),
                             }],
            searched_nodes: 0,
            started_at: SystemTime::now(),
            duration_millis: 0,
            ..self.status
        };

        // Start a new search.
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: position.copy(),
            depth: DEPTH_MAX,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: searchmoves,
            variation_count: variation_count,
        });
    }

    /// Terminates the currently running search (if any), updates the
    /// status.
    pub fn stop(&mut self) {
        self.searcher.terminate_search();
        while !self.status().done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    /// Returns the status of the current search.
    ///
    /// **Important note:** Consecutive calls to this method will
    /// return the same unchanged result. Only after calling `search`,
    /// `stop`, or `wait_status_update`, the result returned by
    /// `status` may change.
    #[inline(always)]
    pub fn status(&self) -> &SearchStatus {
        &self.status
    }

    /// Waits for a search status update, timing out after a specified
    /// duration or earlier.
    pub fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    /// A helper method. It updates the current status according to
    /// the received report message.
    fn process_report(&mut self, report: SearchReport) {
        let duration = self.status.started_at.elapsed().unwrap();
        self.status.duration_millis = 1000 * duration.as_secs() +
                                      (duration.subsec_nanos() / 1000000) as u64;
        self.status.searched_nodes = report.searched_nodes;
        self.status.nps = 1000 * (self.status.nps + self.status.searched_nodes) /
                          (1000 + self.status.duration_millis);
        if self.status.depth < report.depth {
            self.status.depth = report.depth;
            self.status.variations = vec![extract_pv(&self.tt,
                                                     self.position.as_ref(),
                                                     report.depth)];
        }
        self.status.done = report.done;
    }
}
<|endoftext|>


<commit_before>/// The event system will have a trigger function that takes an
/// argument that decides if the event will be triggered this tick
/// or the next tick. The default should be next tick to prevent
/// inadvertent infinite loop bugs.
///
/// The event system can then schedule all events handlers that
/// do not mutate components to be run in parallel.
/// All handlers that do mutate the component list can be run
/// sequentially after that. Perhaps we can even schedule
/// those with non-overlapping mutable component lists to
/// run in parallel.

use std::collections::HashMap;
use std::any::Any;
use shared_mutex::{ SharedMutex };

pub mod example;

lazy_static! {
	pub static ref EventQueues: SharedMutex<HashMap<String, Box<Any+'static+Sync>>> = SharedMutex::new(HashMap::new());
}

/// The event loop should trigger every n ms and execute any
/// events that are queued up. Every event has a function
/// that is called by the trigger! macro that puts the event
/// on the queue.
pub fn run_loop() {

}

pub fn event_queue_push<T>(event_name: &String, event: T) where T: Any+'static+Sync {
	let mut map = EventQueues.write().unwrap();
	if map.contains_key(event_name) {
		let any_value = *map.get_mut(event_name).unwrap();
		let any_value_casted = any_value as Box<Any +'static>;
		let queue : &mut Vec<T> = any_value_casted.downcast_mut::<Vec<T>>().unwrap();
		queue.push(event);
	} else {
		let queue = Box::new(vec![event]);
		map.insert(event_name.clone(), queue);
	}
}
/*
pub fn get_events<T>(event_name: &String) -> Box<Vec<T>> where T: Any+Sync {
	let map = EventQueues.read().unwrap();
	let any_value = **map.get(event_name).unwrap();
	let queue : Vec<T> = any_value.downcast_ref::<Vec<T>>().unwrap();
	return *(map.get(event_name).unwrap());
}*/

/// Defines an event.
macro_rules! event {
	() => ()
}

/// Queues an event to be dispatched.
/// This means that the argument is put into the trigger queue for the
/// event and the event handlers will be invoked either at the next run loop.
macro_rules! trigger {
	//
	// Implementation idea: since we can't initialize events implicitly
	// in any way (not even through a rustc plugin atm) maybe we can
	// do it like lazy_static does it and initialize the event on the
	// first invocation of trigger.
	//
	// Initialisation that needs to happen:
	//   The trigger queue for this particular event needs to be initialized
	//   The run loop needs to be handed a reference to the trigger queue
	//
	() => ()
}

<commit_msg>use entry api<commit_after>/// The event system will have a trigger function that takes an
/// argument that decides if the event will be triggered this tick
/// or the next tick. The default should be next tick to prevent
/// inadvertent infinite loop bugs.
///
/// The event system can then schedule all events handlers that
/// do not mutate components to be run in parallel.
/// All handlers that do mutate the component list can be run
/// sequentially after that. Perhaps we can even schedule
/// those with non-overlapping mutable component lists to
/// run in parallel.

use std::collections::HashMap;
use std::any::Any;
use shared_mutex::{ SharedMutex };

pub mod example;

lazy_static! {
	pub static ref EventQueues: SharedMutex<HashMap<String, Box<Any+'static+Sync>>> = SharedMutex::new(HashMap::new());
}

pub fn event_queue_push<T>(event_name: &String, event: T) where T: Any+'static+Sync {
	let mut map = EventQueues.write().unwrap();
	let mut entry = map.entry(event_name.clone()).or_insert(Box::new(Vec::<T>::new()));
	let mut casted_entry = *entry as Box<Any + 'static>;
	let mut vec = casted_entry.downcast_mut::<Vec<T>>().unwrap();
	vec.push(event);
}

/// The event loop should trigger every n ms and execute any
/// events that are queued up. Every event has a function
/// that is called by the trigger! macro that puts the event
/// on the queue.
pub fn run_loop() {

}

/*
pub fn get_events<T>(event_name: &String) -> Box<Vec<T>> where T: Any+Sync {
	let map = EventQueues.read().unwrap();
	let any_value = **map.get(event_name).unwrap();
	let queue : Vec<T> = any_value.downcast_ref::<Vec<T>>().unwrap();
	return *(map.get(event_name).unwrap());
}*/

/// Defines an event.
macro_rules! event {
	() => ()
}

/// Queues an event to be dispatched.
/// This means that the argument is put into the trigger queue for the
/// event and the event handlers will be invoked either at the next run loop.
macro_rules! trigger {
	//
	// Implementation idea: since we can't initialize events implicitly
	// in any way (not even through a rustc plugin atm) maybe we can
	// do it like lazy_static does it and initialize the event on the
	// first invocation of trigger.
	//
	// Initialisation that needs to happen:
	//   The trigger queue for this particular event needs to be initialized
	//   The run loop needs to be handed a reference to the trigger queue
	//
	() => ()
}

<|endoftext|>


<commit_before>use std::fmt;
use stack::Stack;
use evaluate::Evaluate;
use variable::{GetVariable, DummyVariables};
use convert_ref::{TryFromRef, TryIntoRef};

/// Used to specify an `Operand` or an `Evaluator`.
#[derive(Debug, Copy, Clone)]
pub enum Arithm<T, V, E: Evaluate<T>> {
    Operand(T),
    Variable(V),
    Evaluator(E),
}

/// Interpret a [`Reverse Polish notated`] expression (cf. `3 4 +`).
///
/// `Evaluate` method returns the valid result or an [`Evaluate::Err`]
/// if the evaluation fails.
///
/// `Expressions` are constructed from [`str`] the most of the time.
/// Use the [`try_into_ref()`] method to create an `Expression` type,
/// the result contain informations about the possible error at conversion time.
///
/// [`Reverse Polish notated`]: https://en.wikipedia.org/wiki/Reverse_Polish_notation
/// [`Evaluate::Err`]: ../evaluate/trait.Evaluate.html#associatedtype.Err
/// [`str`]: https://doc.rust-lang.org/std/str/index.html
/// [`try_into_ref()`]: ../convert_ref/trait.TryIntoRef.html
#[derive(Debug)]
pub struct Expression<T, V, E: Evaluate<T>> {
    max_stack: usize,
    expr: Vec<Arithm<T, V, E>>,
}

impl<T: Copy, V: Copy, E: Evaluate<T> + Copy> Expression<T, V, E> {
    /// Evaluate `RPN` expressions. Returns the result
    /// or the [`evaluate Error`](../evaluate/trait.Evaluate.html#associatedtype.Err).
    pub fn evaluate(&self) -> Result<T, E::Err> where (): From<V> {
        self.evaluate_with_variables(&DummyVariables::default())
    }

    /// Evaluate `RPN` expressions containing variables. Returns the result
    /// or the [`evaluate Error`](../evaluate/trait.Evaluate.html#associatedtype.Err).
    ///
    /// # Panics
    /// Panics if a variables doesn't exists in the variable container.
    pub fn evaluate_with_variables<I, C>(&self, variables: &C) -> Result<T, E::Err>
        where V: Into<I>,
              C: GetVariable<I, Output=T>
    {
        let mut stack = Stack::with_capacity(self.max_stack);
        for arithm in &self.expr {
            match *arithm {
                Arithm::Operand(operand) => stack.push(operand),
                Arithm::Variable(var) => {
                    let var = variables.get_variable(var.into()).expect("TODO Variable not found!");
                    stack.push(*var)
                },
                Arithm::Evaluator(evaluator) => evaluator.evaluate(&mut stack)?,
            }
        }
        Ok(stack.pop().unwrap())
    }
}

impl<T, V, E: Evaluate<T>> Expression<T, V, E> {
    pub fn from_iter<A, I>(iter: I) -> Result<Expression<T, V, E>,
                                              ExprResult<<E as TryFromRef<A>>::Err,
                                                         <V as TryFromRef<A>>::Err,
                                                         <T as TryFromRef<A>>::Err>>
        where T: TryFromRef<A>,
              V: TryFromRef<A>,
              E: TryFromRef<A>,
              I: IntoIterator<Item=A>
    {
        let final_expr: Result<Vec<_>, _> = iter.into_iter().map(|token| {
            match TryIntoRef::<E>::try_into_ref(&token) {
                Ok(eval) => Ok(Arithm::Evaluator(eval)),
                Err(eval_err) => {
                    match TryIntoRef::<V>::try_into_ref(&token) {
                        Ok(var) => Ok(Arithm::Variable(var)),
                        Err(var_err) => {
                            match TryIntoRef::<T>::try_into_ref(&token) {
                                Ok(op) => Ok(Arithm::Operand(op)),
                                Err(op_err) => Err(ExprResult::InvalidToken {
                                    evaluator: eval_err,
                                    variable: var_err,
                                    operand: op_err,
                                })
                            }
                        }
                    }
                }
            }
        }).collect();
        final_expr.and_then(|final_expr| {
            match Expression::check_validity(&final_expr) {
                Ok(_) => Ok(Expression {
                    max_stack: Expression::compute_stack_max(&final_expr),
                    expr: final_expr
                }),
                Err(err) => Err(ExprResult::OperandErr(err)),
            }
        })
    }
}

/// Used to specify the error during the conversion.
#[derive(Debug, PartialEq)]
pub enum ExprResult<A, B, C> {
    OperandErr(OperandErr),
    InvalidToken {
        evaluator: A,
        variable: B,
        operand: C
    },
}

/// Used to specify an error related to wrong number of operands in expression.
#[derive(Debug, PartialEq)]
pub enum OperandErr {
    TooManyOperands,
    NotEnoughOperand,
}

impl<T, V, E: Evaluate<T>> Expression<T, V, E> {
    fn check_validity(expr: &[Arithm<T, V, E>]) -> Result<(), OperandErr> {
        // TODO https://doc.rust-lang.org/1.2.0/std/result/fn.fold.html
        use self::OperandErr::*;
        let mut num_operands: usize = 0;
        for arithm in expr {
            match *arithm {
                Arithm::Operand(_) | Arithm::Variable(_) => num_operands += 1,
                Arithm::Evaluator(ref evaluator) => {
                    let needed = evaluator.operands_needed();
                    num_operands = num_operands.checked_sub(needed).ok_or(NotEnoughOperand)?;
                    num_operands += evaluator.operands_generated();
                },
            }
        }
        match num_operands {
            0 => Err(NotEnoughOperand),
            1 => Ok(()),
            _ => Err(TooManyOperands),
        }
    }
}

impl<T, V, E: Evaluate<T>> Expression<T, V, E> {
    fn compute_stack_max(expr: &[Arithm<T, V, E>]) -> usize {
        expr.iter().map(|arithm| {
            match *arithm {
                Arithm::Operand(_) | Arithm::Variable(_) => 1,
                Arithm::Evaluator(ref op) => {
                    op.operands_generated() as isize - op.operands_needed() as isize
                }
            }
        }).fold((0, 0isize), |(max, acc_count), count| {
            let acc = (acc_count + count) as usize;
            if acc > max { (acc, acc as isize) } else { (max, acc as isize) }
        }).0
    }
}

impl<T, V, E> fmt::Display for Expression<T, V, E>
    where T: fmt::Display,
          V: fmt::Display,
          E: fmt::Display + Evaluate<T>
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let len = self.expr.len();
        for (i, arithm) in self.expr.iter().enumerate() {
            match *arithm {
                Arithm::Operand(ref operand) => operand.fmt(f)?,
                Arithm::Variable(ref variable) => variable.fmt(f)?,
                Arithm::Evaluator(ref evaluator) => evaluator.fmt(f)?,
            }
            if i != len - 1 {
                f.write_str(" ")?
            }
        }
        Ok(())
    }
}
<commit_msg>doc: Fix evaluate_with_variables doc<commit_after>use std::fmt;
use stack::Stack;
use evaluate::Evaluate;
use variable::{GetVariable, DummyVariables};
use convert_ref::{TryFromRef, TryIntoRef};

/// Used to specify an `Operand` or an `Evaluator`.
#[derive(Debug, Copy, Clone)]
pub enum Arithm<T, V, E: Evaluate<T>> {
    Operand(T),
    Variable(V),
    Evaluator(E),
}

/// Interpret a [`Reverse Polish notated`] expression (cf. `3 4 +`).
///
/// `Evaluate` method returns the valid result or an [`Evaluate::Err`]
/// if the evaluation fails.
///
/// `Expressions` are constructed from [`str`] the most of the time.
/// Use the [`try_into_ref()`] method to create an `Expression` type,
/// the result contain informations about the possible error at conversion time.
///
/// [`Reverse Polish notated`]: https://en.wikipedia.org/wiki/Reverse_Polish_notation
/// [`Evaluate::Err`]: ../evaluate/trait.Evaluate.html#associatedtype.Err
/// [`str`]: https://doc.rust-lang.org/std/str/index.html
/// [`try_into_ref()`]: ../convert_ref/trait.TryIntoRef.html
#[derive(Debug)]
pub struct Expression<T, V, E: Evaluate<T>> {
    max_stack: usize,
    expr: Vec<Arithm<T, V, E>>,
}

#[derive(Debug)]
pub enum EvalErr<V, E> {
    VariableNotFound(V),
    EvalError(E),
}

impl<T: Copy, V: Copy, E: Evaluate<T> + Copy> Expression<T, V, E> {
    /// Evaluate `RPN` expressions. Returns the result
    /// or the [`evaluate Error`](../evaluate/trait.Evaluate.html#associatedtype.Err).
    pub fn evaluate(&self) -> Result<T, E::Err> where (): From<V> {
        self.evaluate_with_variables(&DummyVariables::default())
    }

    /// Evaluate `RPN` expressions containing variables. Returns the result
    /// or the [`evaluate Error`](../evaluate/trait.Evaluate.html#associatedtype.Err).
    pub fn evaluate_with_variables<I, C>(&self, variables: &C) -> Result<T, EvalErr<V, E::Err>>
        where V: Into<I>,
              C: GetVariable<I, Output=T>
    {
        let mut stack = Stack::with_capacity(self.max_stack);
        for arithm in &self.expr {
            match *arithm {
                Arithm::Operand(operand) => stack.push(operand),
                Arithm::Variable(var) => {
                    let var = variables.get_variable(var.into()).expect("TODO Variable not found!");
                    stack.push(*var)
                },
                Arithm::Evaluator(evaluator) => evaluator.evaluate(&mut stack)?,
            }
        }
        Ok(stack.pop().unwrap())
    }
}

impl<T, V, E: Evaluate<T>> Expression<T, V, E> {
    pub fn from_iter<A, I>(iter: I) -> Result<Expression<T, V, E>,
                                              ExprResult<<E as TryFromRef<A>>::Err,
                                                         <V as TryFromRef<A>>::Err,
                                                         <T as TryFromRef<A>>::Err>>
        where T: TryFromRef<A>,
              V: TryFromRef<A>,
              E: TryFromRef<A>,
              I: IntoIterator<Item=A>
    {
        let final_expr: Result<Vec<_>, _> = iter.into_iter().map(|token| {
            match TryIntoRef::<E>::try_into_ref(&token) {
                Ok(eval) => Ok(Arithm::Evaluator(eval)),
                Err(eval_err) => {
                    match TryIntoRef::<V>::try_into_ref(&token) {
                        Ok(var) => Ok(Arithm::Variable(var)),
                        Err(var_err) => {
                            match TryIntoRef::<T>::try_into_ref(&token) {
                                Ok(op) => Ok(Arithm::Operand(op)),
                                Err(op_err) => Err(ExprResult::InvalidToken {
                                    evaluator: eval_err,
                                    variable: var_err,
                                    operand: op_err,
                                })
                            }
                        }
                    }
                }
            }
        }).collect();
        final_expr.and_then(|final_expr| {
            match Expression::check_validity(&final_expr) {
                Ok(_) => Ok(Expression {
                    max_stack: Expression::compute_stack_max(&final_expr),
                    expr: final_expr
                }),
                Err(err) => Err(ExprResult::OperandErr(err)),
            }
        })
    }
}

/// Used to specify the error during the conversion.
#[derive(Debug, PartialEq)]
pub enum ExprResult<A, B, C> {
    OperandErr(OperandErr),
    InvalidToken {
        evaluator: A,
        variable: B,
        operand: C
    },
}

/// Used to specify an error related to wrong number of operands in expression.
#[derive(Debug, PartialEq)]
pub enum OperandErr {
    TooManyOperands,
    NotEnoughOperand,
}

impl<T, V, E: Evaluate<T>> Expression<T, V, E> {
    fn check_validity(expr: &[Arithm<T, V, E>]) -> Result<(), OperandErr> {
        // TODO https://doc.rust-lang.org/1.2.0/std/result/fn.fold.html
        use self::OperandErr::*;
        let mut num_operands: usize = 0;
        for arithm in expr {
            match *arithm {
                Arithm::Operand(_) | Arithm::Variable(_) => num_operands += 1,
                Arithm::Evaluator(ref evaluator) => {
                    let needed = evaluator.operands_needed();
                    num_operands = num_operands.checked_sub(needed).ok_or(NotEnoughOperand)?;
                    num_operands += evaluator.operands_generated();
                },
            }
        }
        match num_operands {
            0 => Err(NotEnoughOperand),
            1 => Ok(()),
            _ => Err(TooManyOperands),
        }
    }
}

impl<T, V, E: Evaluate<T>> Expression<T, V, E> {
    fn compute_stack_max(expr: &[Arithm<T, V, E>]) -> usize {
        expr.iter().map(|arithm| {
            match *arithm {
                Arithm::Operand(_) | Arithm::Variable(_) => 1,
                Arithm::Evaluator(ref op) => {
                    op.operands_generated() as isize - op.operands_needed() as isize
                }
            }
        }).fold((0, 0isize), |(max, acc_count), count| {
            let acc = (acc_count + count) as usize;
            if acc > max { (acc, acc as isize) } else { (max, acc as isize) }
        }).0
    }
}

impl<T, V, E> fmt::Display for Expression<T, V, E>
    where T: fmt::Display,
          V: fmt::Display,
          E: fmt::Display + Evaluate<T>
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let len = self.expr.len();
        for (i, arithm) in self.expr.iter().enumerate() {
            match *arithm {
                Arithm::Operand(ref operand) => operand.fmt(f)?,
                Arithm::Variable(ref variable) => variable.fmt(f)?,
                Arithm::Evaluator(ref evaluator) => evaluator.fmt(f)?,
            }
            if i != len - 1 {
                f.write_str(" ")?
            }
        }
        Ok(())
    }
}
<|endoftext|>