<commit_before>//! Implements Dominance frontier and dominance tree computation.

#![allow(dead_code, unused_variables, unused_imports, unused_mut)]

use std::collections::{BTreeSet, BTreeMap, HashMap};
use petgraph::graph::{Graph, NodeIndex, EdgeIndex, Edge};
use petgraph::EdgeDirection;
use petgraph::{Dfs, Bfs};
use super::super::middle::dot::{GraphDot, EdgeInfo, Label};

//use super::dot::{GraphDot, EdgeInfo, Label};

// Non-public struct. Not intended to be used outside.
// The index in the DomTree = postorder numbering of the node in the original cfg.
type DomTreeIndex = usize;

pub struct DomTree {
    idom: Vec<usize>,
    // map from the internal numbering to original NodeIndex in cfg.
    map: HashMap<usize, NodeIndex>,
    // map from original NodeIndex in the cfg to internal numbering.
    rmap: HashMap<NodeIndex, usize>,
    g: Graph<NodeIndex, u8>,
}

#[derive(Debug)]
pub struct DFSVisitor {
    pub post_order: Vec<NodeIndex>,
    pub pre_order: Vec<NodeIndex>,
    visited: Vec<NodeIndex>,
}

impl DFSVisitor {
    pub fn new() -> DFSVisitor {
        DFSVisitor {
            post_order: Vec::new(),
            pre_order: Vec::new(),
            visited: Vec::new(),
        }
    }

    pub fn dfs<N, E>(&mut self, g: &Graph<N, E>, node: NodeIndex) {
        // If this node is already visited, return.
        if self.visited.contains(&node) {
            return;
        }

        self.pre_order.push(node.clone());

        // recursive call to perform dfs on children
        let neighbors_iter = g.neighbors_directed(node, EdgeDirection::Outgoing)
                              .collect::<Vec<NodeIndex>>();

        for n in neighbors_iter.iter() {
            self.dfs(g, n.clone());
        }

        self.visited.push(node.clone());
        self.post_order.push(node.clone());

    }
}

//trait PostOrder {
    //fn post_order(&self, start_node: usize) -> Vec<NodeIndex>;
    //fn dfs(&self, node: NodeIndex) -> Vec<NodeIndex>;
//}

//impl<N, E> PostOrder for Graph<N, E> {
    //fn post_order(&self, start_node: usize) -> Vec<NodeIndex> {
        //let n0 = NodeIndex::new(start_node);
        //let count = self.node_count();
        //let res = self.dfs(n0);

        //// If all nodes have been visited, return the resulting vector.
        //if res.count() == count {
            //return res;
        //}

        //// Else, visit the other nodes.
        //for i in 0..count {
            //if !res.contains(i) {
                //self.dfs(NodeIndex::new(i))
                    //.iter()
                    //.map(|e| res.push(e.clone()));
            //}
        //}

        //return res;
    //}

//}

// Note: No method should leak out or accept the internal numbering.
impl DomTree {
    fn new() -> DomTree {
        DomTree {
            idom: Vec::new(),
            map: HashMap::new(),
            rmap: HashMap::new(),
            g: Graph::new(),
        }
    }

    pub fn doms(&self, i: NodeIndex) -> Vec<NodeIndex> {
        Vec::new()
    }

    pub fn idom(&self, i: NodeIndex) -> NodeIndex {
        let internal_index = self.rmap.get(&i).unwrap();
        let internal_node = self.idom[*internal_index];
        self.map.get(&internal_node).unwrap().clone()
    }

    fn intersect(idom: &Vec<usize>, i: &usize, j: &usize) -> usize {
        let mut f1 = *i;
        let mut f2 = *j;
        while f1 != f2 {
            while f1 < f2 {
                f1 = idom[f1];
            }
            while f2 < f1 {
                f2 = idom[f2];
            }
        }
        return f1;
    }

    //fn postorder<N, E>(g: &Graph<N, E>, start_node: NodeIndex) -> Vec<NodeIndex> {

    //}

    pub fn build_dom_tree<N, E>(g: &Graph<N, E>, start_node: NodeIndex) -> DomTree {
        let mut tree = DomTree::new();
        {
            let idom = &mut (tree.idom);
            let map = &mut (tree.map);
            let rmap = &mut (tree.rmap);
            let domg = &mut (tree.g);

            let mut v = DFSVisitor::new();
            let node_count = g.node_count();
            let invalid = node_count;

            // Compute the post-order for 'g'.
            v.dfs(&g,start_node);

            // In case the graph is disconnected (usually not the case with cfgs), visit all the
            // unvisited nodes.
            for i in 0..node_count {
                v.dfs(&g, NodeIndex::new(i));
            }

            let post_order = v.post_order.clone();

            // Tuple of (NodeIndex, post-order numbering).
            let mut nodes_iter = post_order.iter()
                                       .cloned()
                                       .zip(0..node_count)
                                       .collect::<Vec<(NodeIndex, usize)>>();

            let mut start_index = 0;
            let mut domg_nodes: Vec<NodeIndex> = Vec::new();
            for item in nodes_iter.iter() {
                if item.0 == start_node {
                    idom.push(item.1);
                    start_index = item.1;
                } else {
                    idom.push(invalid.clone());
                }

                domg_nodes.push(domg.add_node(item.0.clone()));
                //map.insert(item.1, item.0.clone());
                rmap.insert(item.0.clone(), item.1);
            }

            nodes_iter.remove(start_index);
            let mut changed = true;
            nodes_iter.reverse();
            while changed {
                changed = false;
                for n in nodes_iter.iter() {
                    let node: NodeIndex = n.0;
                    let mut preds = g.neighbors_directed(node.clone(), EdgeDirection::Incoming)
                                     .map(|x| rmap.get(&x).unwrap().clone())
                                     .collect::<Vec<usize>>();
                    let mut pindex = 0;
                    for i in 0..preds.len() {
                        let p = preds[i];
                        if idom[p] < invalid {
                            pindex = i;
                            break;
                        }
                    }

                    let mut new_idom = preds[pindex];
                    preds.remove(pindex);

                    for p in preds.iter() {
                        if idom[*p] != invalid {
                            new_idom = DomTree::intersect(&idom, &new_idom, p);
                        }
                    }

                    if idom[n.1] != new_idom {
                        idom[n.1] = new_idom;
                        changed = true;
                    }
                }
            }

            for i in 0..idom.len() {
                if i == idom[i] { continue; }
                domg.add_edge(domg_nodes[idom[i]], domg_nodes[i], 0);
            }

        }

        tree
    }

}

///////////////////////////////////////////////////////////////////////////////
//// Implementation of Traits to emit dot for dom.
///////////////////////////////////////////////////////////////////////////////

impl GraphDot for DomTree {
    type NodeType = NodeIndex;
    type EdgeType = Edge<u8>;

    fn configure(&self) -> String {
        format!("digraph cfg {{\nsplines=\"true\";\n")
    }

    fn nodes(&self) -> Vec<Self::NodeType> {
        let res = self.g.raw_nodes().iter().map(|e| e.weight.clone()).collect();
        res
    }

    fn edges(&self) -> Vec<Self::EdgeType> {
        let res = self.g.raw_edges().to_vec();
        res
    }

    fn get_node(&self, n: usize) -> Option<&Self::NodeType> {
        self.g.node_weight(NodeIndex::new(n))
    }
}

impl EdgeInfo for Edge<u8> {
    fn source(&self) -> usize {
        self.source().index()
    }

    fn target(&self) -> usize {
        self.target().index()
    }
}

impl Label for Edge<u8> {
    fn label(&self) -> String {
        ";\n".to_string()
    }

    fn name(&self) -> Option<String> {
        None
    }
}

impl Label for NodeIndex {
    fn label(&self) -> String {
        let tmp = format!("n{}", self.index());
        format!("{} [label={}];\n", tmp, tmp)
    }

    fn name(&self) -> Option<String> {
        Some(format!("n{}", self.index()))
    }
}

#[cfg(test)]
mod test {

    use super::*;
    use petgraph::graph::{Graph, NodeIndex, EdgeIndex};
    use petgraph::{Dfs};
    use std::io::prelude::*;
    use std::fs::File;

    use super::super::super::middle::dot;

    #[test]
    fn dfs() {
        let mut g = Graph::<u64, u64>::new();
        let mut n: Vec<NodeIndex> = Vec::new();
        for i in 0..13 {
            n.push(g.add_node(i));
        }

        g.add_edge(n[0] , n[6] , 0);
        g.add_edge(n[0] , n[1] , 0);
        g.add_edge(n[0] , n[5] , 0);
        g.add_edge(n[5] , n[4] , 0);
        g.add_edge(n[2] , n[3] , 0);
        g.add_edge(n[2] , n[0] , 0);
        g.add_edge(n[3] , n[5] , 0);
        g.add_edge(n[6] , n[4] , 0);
        g.add_edge(n[6] , n[9] , 0);
        g.add_edge(n[9] , n[10], 0);
        g.add_edge(n[9] , n[11], 0);
        g.add_edge(n[9] , n[12], 0);
        g.add_edge(n[11], n[12], 0);
        g.add_edge(n[8] , n[7] , 0);
        g.add_edge(n[7] , n[6] , 0);

        let count = g.node_count();

        let mut dfs = DFSVisitor::new();
        for i in 0..count {
            dfs.dfs(&g, n[i]);
        }

        let order = vec![4, 5, 1, 12, 11, 10, 9, 6, 0, 3, 2, 7, 8];
        for (i, j) in dfs.post_order.iter().zip(order) {
            assert_eq!(i.index(), j)
        }

        let order = vec![0, 5, 4, 1, 6, 9, 12, 11, 10, 2, 3, 7, 8];
        for (i, j) in dfs.pre_order.iter().zip(order) {
            assert_eq!(i.index(), j)
        }
    }

    #[test]
    fn dom_new() {
        let mut g = Graph::<NodeIndex, u64>::new();
        let n0 = g.add_node(NodeIndex::new(0));
        let n1 = g.add_node(NodeIndex::new(1));
        let n2 = g.add_node(NodeIndex::new(2));
        let n3 = g.add_node(NodeIndex::new(3));
        let n4 = g.add_node(NodeIndex::new(4));
        let n5 = g.add_node(NodeIndex::new(5));
        let n6 = g.add_node(NodeIndex::new(6));
        let n7 = g.add_node(NodeIndex::new(7));
        let n8 = g.add_node(NodeIndex::new(8));
        let n9 = g.add_node(NodeIndex::new(9));

        g.add_edge(n0, n2, 0);
        g.add_edge(n2, n3, 0);
        g.add_edge(n2, n4, 0);
        g.add_edge(n4, n3, 0);
        g.add_edge(n4, n5, 0);
        g.add_edge(n5, n3, 0);
        g.add_edge(n5, n6, 0);
        g.add_edge(n6, n8, 0);
        g.add_edge(n6, n9, 0);
        g.add_edge(n8, n7, 0);
        g.add_edge(n9, n7, 0);
        g.add_edge(n3, n7, 0);
        g.add_edge(n7, n1, 0);

        let dom = DomTree::build_dom_tree(&mut g, n0);

        let _dot = dot::emit_dot(&dom);
        let mut dot_file = File::create("dom-new.dot").ok().expect("Error. Cannot create file!\n");
        dot_file.write_all(_dot.as_bytes()).ok().expect("Error. Cannot write file!\n");
    }
}
<commit_msg>[WIP] DomTree cleanup + docs<commit_after>//! Implements Dominance frontier and dominance tree computation.

use std::collections::{HashMap};
use petgraph::graph::{Graph, NodeIndex, Edge};
use petgraph::EdgeDirection;
use super::super::middle::dot::{GraphDot, EdgeInfo, Label};

pub struct DomTree {
    idom: Vec<usize>,
    // map from the internal numbering to original NodeIndex in cfg.
    map: HashMap<usize, NodeIndex>,
    // map from original NodeIndex in the cfg to internal numbering.
    rmap: HashMap<NodeIndex, usize>,
    g: Graph<NodeIndex, u8>,
}

#[derive(Debug)]
pub struct DFSVisitor {
    post_order: Vec<NodeIndex>,
    pre_order: Vec<NodeIndex>,
    visited: Vec<NodeIndex>,
}

impl DFSVisitor {
    pub fn new() -> DFSVisitor {
        DFSVisitor {
            post_order: Vec::new(),
            pre_order: Vec::new(),
            visited: Vec::new(),
        }
    }

    pub fn dfs<N, E>(&mut self, g: &Graph<N, E>, node: NodeIndex) {
        if self.visited.contains(&node) {
            return;
        }

        self.pre_order.push(node.clone());
        let neighbors_iter = g.neighbors_directed(node, EdgeDirection::Outgoing)
                              .collect::<Vec<NodeIndex>>();

        for n in neighbors_iter.iter() {
            self.dfs(g, n.clone());
        }

        self.visited.push(node.clone());
        self.post_order.push(node.clone());
    }

    pub fn post_order(&self) -> Vec<NodeIndex> {
        self.post_order.clone()
    }

    pub fn pre_order(&self) -> Vec<NodeIndex> {
        self.pre_order.clone()
    }
}

// Note: No method should leak out or accept the internal numbering.
impl DomTree {
    fn new() -> DomTree {
        DomTree {
            idom: Vec::new(),
            map: HashMap::new(),
            rmap: HashMap::new(),
            g: Graph::new(),
        }
    }

    pub fn graph(&self) -> &Graph<NodeIndex, u8> {
        &self.g
    }

    pub fn doms(&self, i: NodeIndex) -> Vec<NodeIndex> {
        let internal_index = self.rmap.get(&i).unwrap();
        let mut idom = *internal_index;
        let mut doms = Vec::<usize>::new();
        while idom != self.idom[idom] {
            doms.push(idom.clone());
            idom = self.idom[idom];
        }

        doms.push(idom.clone());
        doms.iter().map(|d| self.map.get(d).unwrap().clone()).collect()
    }

    pub fn idom(&self, i: NodeIndex) -> NodeIndex {
        let internal_index = self.rmap.get(&i).unwrap();
        let internal_node = self.idom[*internal_index];
        self.map.get(&internal_node).unwrap().clone()
    }

    fn intersect(idom: &Vec<usize>, i: &usize, j: &usize) -> usize {
        let mut f1 = *i;
        let mut f2 = *j;
        while f1 != f2 {
            while f1 < f2 {
                f1 = idom[f1];
            }
            while f2 < f1 {
                f2 = idom[f2];
            }
        }
        return f1;
    }

    pub fn build_dom_tree<N, E>(g: &Graph<N, E>, start_node: NodeIndex) -> DomTree {
        let mut tree = DomTree::new();

        {
            let idom = &mut (tree.idom);
            let map = &mut (tree.map);
            let rmap = &mut (tree.rmap);
            let dom_tree = &mut (tree.g);

            let mut v = DFSVisitor::new();
            let node_count = g.node_count();
            let invalid = node_count;

            // Compute the post-order for 'g'.
            v.dfs(&g,start_node);

            // In case the graph is disconnected (usually not the case with cfgs), visit all the
            // unvisited nodes.
            for i in 0..node_count {
                v.dfs(&g, NodeIndex::new(i));
            }

            // Tuple of (NodeIndex, post-order numbering).
            let mut nodes_iter = v.post_order().iter()
                                               .cloned()
                                               .zip(0..node_count)
                                               .collect::<Vec<(NodeIndex, usize)>>();

            let mut start_index = 0;
            let mut dom_tree_nodes: Vec<NodeIndex> = Vec::new();

            for item in nodes_iter.iter() {
                if item.0 == start_node {
                    idom.push(item.1);
                    start_index = item.1;
                } else {
                    idom.push(invalid.clone());
                }

                dom_tree_nodes.push(dom_tree.add_node(NodeIndex::new(item.1)));
                rmap.insert(item.0.clone(), item.1);
                map.insert(item.1, item.0.clone());
            }

            nodes_iter.remove(start_index);
            nodes_iter.reverse();

            let mut changed = true;
            let mut preds_map: HashMap<NodeIndex, Vec<usize>> = HashMap::new();

            while changed {
                changed = false;
                for n in nodes_iter.iter() {
                    let node: NodeIndex = n.0;
                    let preds_iter = g.neighbors_directed(node.clone(), EdgeDirection::Incoming)
                                      .map(|x| rmap.get(&x).unwrap().clone());
                    let preds =  preds_map.entry(node.clone())
                                          .or_insert(preds_iter.collect::<Vec<usize>>());

                    let mut new_idom = preds[0];
                    for p in preds.iter() {
                        if idom[*p] < invalid {
                            new_idom = *p;
                            break;
                        }
                    }

                    for p in preds.iter() {
                        if idom[*p] != invalid {
                            new_idom = DomTree::intersect(&idom, &new_idom, p);
                        }
                    }

                    if idom[n.1] != new_idom {
                        idom[n.1] = new_idom;
                        changed = true;
                    }
                }
            }

            for i in 0..idom.len() {
                if i == idom[i] { continue; }
                let n1 = dom_tree_nodes[map.get(&idom[i]).unwrap().index()];
                let n2 = dom_tree_nodes[map.get(&i).unwrap().index()];
                dom_tree.add_edge(n1, n2, 0);
            }
        }
        return tree;
    }
}

///////////////////////////////////////////////////////////////////////////////
//// Implementation of Traits to emit dot for dom.
///////////////////////////////////////////////////////////////////////////////

impl GraphDot for DomTree {
    type NodeType = NodeIndex;
    type EdgeType = Edge<u8>;

    fn configure(&self) -> String {
        format!("digraph cfg {{\nsplines=\"true\";\n")
    }

    fn nodes(&self) -> Vec<Self::NodeType> {
        let res = self.g.raw_nodes().iter().map(|e| e.weight.clone()).collect();
        res
    }

    fn edges(&self) -> Vec<Self::EdgeType> {
        let res = self.g.raw_edges().to_vec();
        res
    }

    fn get_node(&self, n: usize) -> Option<&Self::NodeType> {
        self.g.node_weight(NodeIndex::new(n))
    }
}

impl EdgeInfo for Edge<u8> {
    fn source(&self) -> usize {
        self.source().index()
    }

    fn target(&self) -> usize {
        self.target().index()
    }
}

impl Label for Edge<u8> {
    fn label(&self) -> String {
        ";\n".to_string()
    }

    fn name(&self) -> Option<String> {
        None
    }
}

impl Label for NodeIndex {
    fn label(&self) -> String {
        let tmp = format!("n{}", self.index());
        format!("{} [label={}];\n", tmp, tmp)
    }

    fn name(&self) -> Option<String> {
        Some(format!("n{}", self.index()))
    }
}

#[cfg(test)]
mod test {

    use super::*;
    use petgraph::graph::{Graph, NodeIndex};

    #[test]
    fn dfs() {
        let mut g = Graph::<u64, u64>::new();
        let mut n: Vec<NodeIndex> = Vec::new();
        for i in 0..13 {
            n.push(g.add_node(i));
        }

        g.add_edge(n[0] , n[6] , 0);
        g.add_edge(n[0] , n[1] , 0);
        g.add_edge(n[0] , n[5] , 0);
        g.add_edge(n[5] , n[4] , 0);
        g.add_edge(n[2] , n[3] , 0);
        g.add_edge(n[2] , n[0] , 0);
        g.add_edge(n[3] , n[5] , 0);
        g.add_edge(n[6] , n[4] , 0);
        g.add_edge(n[6] , n[9] , 0);
        g.add_edge(n[9] , n[10], 0);
        g.add_edge(n[9] , n[11], 0);
        g.add_edge(n[9] , n[12], 0);
        g.add_edge(n[11], n[12], 0);
        g.add_edge(n[8] , n[7] , 0);
        g.add_edge(n[7] , n[6] , 0);

        let count = g.node_count();

        let mut dfs = DFSVisitor::new();
        for i in 0..count {
            dfs.dfs(&g, n[i]);
        }

        let order = vec![4, 5, 1, 12, 11, 10, 9, 6, 0, 3, 2, 7, 8];
        for (i, j) in dfs.post_order().iter().zip(order) {
            assert_eq!(i.index(), j)
        }

        let order = vec![0, 5, 4, 1, 6, 9, 12, 11, 10, 2, 3, 7, 8];
        for (i, j) in dfs.pre_order().iter().zip(order) {
            assert_eq!(i.index(), j)
        }
    }

    #[test]
    fn dom() {
        let mut g = Graph::<NodeIndex, u64>::new();
        let n0 = g.add_node(NodeIndex::new(0));
        let n1 = g.add_node(NodeIndex::new(1));
        let n2 = g.add_node(NodeIndex::new(2));
        let n3 = g.add_node(NodeIndex::new(3));
        let n4 = g.add_node(NodeIndex::new(4));
        let n5 = g.add_node(NodeIndex::new(5));
        let n6 = g.add_node(NodeIndex::new(6));
        let n7 = g.add_node(NodeIndex::new(7));
        let n8 = g.add_node(NodeIndex::new(8));
        let n9 = g.add_node(NodeIndex::new(9));

        g.add_edge(n0, n2, 0);
        g.add_edge(n2, n3, 0);
        g.add_edge(n2, n4, 0);
        g.add_edge(n4, n3, 0);
        g.add_edge(n4, n5, 0);
        g.add_edge(n5, n3, 0);
        g.add_edge(n5, n6, 0);
        g.add_edge(n6, n8, 0);
        g.add_edge(n6, n9, 0);
        g.add_edge(n8, n7, 0);
        g.add_edge(n9, n7, 0);
        g.add_edge(n3, n7, 0);
        g.add_edge(n7, n1, 0);

        let dom = DomTree::build_dom_tree(&mut g, n0);
        let doms_n1 = [n1, n7, n2, n0];
        let doms_n9 = [n9, n6, n5, n4, n2, n0];
        let doms_n3 = [n3, n2, n0];

        assert_eq!(dom.doms(n1), doms_n1);
        assert_eq!(dom.doms(n9), doms_n9);
        assert_eq!(dom.doms(n3), doms_n3);
    }
}
<|endoftext|>


<commit_before>use super::ease::easing;
struct Bounce;

impl easing for Bounce{
	fn ease_in(t: f32, b: f32, c: f32, d: f32)->f32{
	0.0
	}

	fn ease_out(t: f32, b:f32, c:f32, d:f32)->f32{
	0.0
	}

	fn ease_in_out(t: f32, b:f32, c:f32, d:f32)->f32{
	0.0
	}

}
<commit_msg>bounce ease in and out implemented<commit_after>use super::ease::easing;
struct Bounce;

impl easing for Bounce {
    fn ease_in(t: f32, b: f32, c: f32, d: f32) -> f32 {
        c - Bounce::ease_out(d-t, 0.0, c, d)
    }

    fn ease_out(t: f32, b: f32, c: f32, d: f32) -> f32 {
        0.0
    }

    fn ease_in_out(t: f32, b: f32, c: f32, d: f32) -> f32 {
        if t < (d / 2.0) {
            Bounce::ease_in(t * 2.0, 0.0, c, d) * 0.5 + b
        } else {
            Bounce::ease_out(t * 2.0 - d, 0.0, c, d) * 0.5 + c * 0.5 + b
        }

    }
}
<|endoftext|>


<commit_before>use na::{Vector2, Vector4, Matrix2};
use unicode_normalization;
use rusttype::{FontCollection, Font, Scale, point, vector, PositionedGlyph};
use rusttype::gpu_cache::{Cache};
use rusttype;
use rusttype::Rect;
use glium;
use glium::{Surface, Frame, DrawParameters};
use glium::backend::glutin_backend::GlutinFacade;
use std::borrow::Cow;
use super::{RenderText, TextVertex};
use rendering;
use rendering::shaders;
use rendering::glium_buffer::GliumBuffer;
use games::view_details;

pub const OPEN_SANS: &'static[u8] = include_bytes!("OpenSans.ttf");

pub struct TextBuffer<'a, T: RenderText<'a>> {
    pub text_objects: Option<Vec<T>>,
    vertices: Vec<TextVertex>,
    text_cache: rusttype::gpu_cache::Cache,
    program: glium::Program,
    cache_tex: glium::texture::Texture2d,
    font: Font<'a>,
}

impl<'a, T: RenderText<'a>> TextBuffer<'a, T> {
    pub fn new(display: Box<GlutinFacade>) -> Self {
        let dpi_factor = display.get_window().unwrap().hidpi_factor();

        let (cache_width, cache_height) = (512 * dpi_factor as u32, 512 * dpi_factor as u32);
        let cache = rusttype::gpu_cache::Cache::new(cache_width, cache_height, 0.1, 0.1);
        let cache_tex = glium::texture::Texture2d::with_format(
            &*display,
            glium::texture::RawImage2d {
                data: Cow::Owned(vec![0u8; cache_width as usize * cache_height as usize]),
                width: cache_width,
                height: cache_height,
                format: glium::texture::ClientFormat::U8
            },
            glium::texture::UncompressedFloatFormat::U8,
            glium::texture::MipmapsOption::NoMipmap).unwrap();

        TextBuffer {
            text_objects: None,
            vertices: Vec::new(),
            text_cache: cache,
            cache_tex: cache_tex,
            program: shaders::make_program_from_shaders(T::get_shaders(), &display),
            font: FontCollection::from_bytes(OPEN_SANS).into_font().unwrap(),
        }
    }

    fn load_text_into_cache<Text: RenderText<'a>>(
        &mut self,
        render_text: Text,
        glyph_scale: Scale)
    {
        let glyphs = layout_paragraph(&self.font, glyph_scale, &render_text.get_content());
        for glyph in &glyphs {
            self.text_cache.queue_glyph(0, glyph.clone());
        }

        let cache_tex = &mut self.cache_tex;
        let mut text_cache = &mut self.text_cache;
        text_cache.cache_queued(
            |rect, data| {
                cache_tex.main_level().write(glium::Rect {
                    left: rect.min.x,
                    bottom: rect.min.y,
                    width: rect.width(),
                    height: rect.height()
                }, glium::texture::RawImage2d {
                    data: Cow::Borrowed(data),
                    width: rect.width(),
                    height: rect.height(),
                    format: glium::texture::ClientFormat::U8
                });
            },
            1).unwrap();

        let glyph_pos_data: Vec<(Rect<f32>, Rect<i32>)> = glyphs
            .iter()
            .filter_map(|g| {
                if let Ok(Some(pos_data)) = text_cache.rect_for(0, g) {
                    Some(pos_data)
                } else {
                    None
                }}).collect();

        let mut vertices = render_text.get_vertices(glyph_pos_data, glyph_scale);
        self.vertices.append(&mut vertices);
    }

    fn render<Unif: glium::uniforms::Uniforms>(
        vertices: &Vec<TextVertex>,
        program: &glium::Program,
        target: &mut Frame,
        display: &GlutinFacade,
        draw_params: &DrawParameters,
        uniforms: &Unif)
    {
        let vertex_buffer = glium::VertexBuffer::new(
            display,
            &vertices).unwrap();

        target.draw(&vertex_buffer,
                    glium::index::NoIndices(glium::index::PrimitiveType::Points),
                    &program,
                    uniforms,
                    &glium::DrawParameters {
                        blend: glium::Blend::alpha_blending(),
                        ..Default::default()
                    }).unwrap();

    }
}

impl<'a, T: RenderText<'a>> GliumBuffer<T> for TextBuffer<'a, T> {
    fn draw_at_target<Unif: glium::uniforms::Uniforms>(
        &mut self,
        target: &mut Frame,
        display: &GlutinFacade,
        view_details: view_details::ViewDetails,
        draw_params: &DrawParameters,
        _: &Unif
    ) {
        let buffer = self.text_objects.take();

        if let Some(buffer) = buffer {
            let (width, height) = target.get_dimensions();
            let dpi_factor = display.get_window().unwrap().hidpi_factor();

            let glyph_scale = Scale::uniform(256.0 * dpi_factor);

            for render_text in buffer {
                self.load_text_into_cache(render_text, glyph_scale);
            }

            let aspect_ratio = width as f64 / height as f64;

            let cache_tex = &self.cache_tex;
            let uniforms = uniform! {
                tex: cache_tex.sampled().magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
                screen_width: width,
                screen_height: height,
                aspect_ratio: aspect_ratio as f32,
                world_view: rendering::glium_renderer::GliumRenderer::create_worldview_mat(view_details, aspect_ratio)
            };

            Self::render(&self.vertices, &self.program, target, display, draw_params, &uniforms);
        }
    }

    fn load_renderable(&mut self, text: T) {
        if let Some(ref mut buffer) = self.text_objects {
            buffer.push(text);
        }
        else {
            self.text_objects = Some(vec![text]);
        }
    }

    fn flush_buffer(&mut self) {
        self.text_objects = None;
    }
}

fn layout_paragraph<'a>(font: &'a Font,
                        scale: Scale,
                        text: &str) -> Vec<PositionedGlyph<'a>> {
    use unicode_normalization::UnicodeNormalization;
    let mut result = Vec::new();
    let v_metrics = font.v_metrics(scale);
    let advance_height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap;
    let mut caret = point(0.0, v_metrics.ascent);
    let mut last_glyph_id = None;
    for c in text.nfc() {
        if c.is_control() {
            match c {
                '\r' => {
                    caret = point(0.0, caret.y + advance_height);
                }
                '\n' => {},
                _ => {}
            }
            continue;
        }
        let base_glyph = if let Some(glyph) = font.glyph(c) {
            glyph
        } else {
            continue;
        };
        if let Some(id) = last_glyph_id.take() {
            caret.x += font.pair_kerning(scale, id, base_glyph.id());
        }
        last_glyph_id = Some(base_glyph.id());
        let glyph = base_glyph.scaled(scale).positioned(caret);
        caret.x += glyph.unpositioned().h_metrics().advance_width;
        result.push(glyph);
    }
    result
}
<commit_msg>Text Bugfix<commit_after>use na::{Vector2, Vector4, Matrix2};
use unicode_normalization;
use rusttype::{FontCollection, Font, Scale, point, vector, PositionedGlyph};
use rusttype::gpu_cache::{Cache};
use rusttype;
use rusttype::Rect;
use glium;
use glium::{Surface, Frame, DrawParameters};
use glium::backend::glutin_backend::GlutinFacade;
use std::borrow::Cow;
use super::{RenderText, TextVertex};
use rendering;
use rendering::shaders;
use rendering::glium_buffer::GliumBuffer;
use games::view_details;

pub const OPEN_SANS: &'static[u8] = include_bytes!("OpenSans.ttf");

pub struct TextBuffer<'a, T: RenderText<'a>> {
    pub text_objects: Option<Vec<T>>,
    vertices: Vec<TextVertex>,
    text_cache: rusttype::gpu_cache::Cache,
    program: glium::Program,
    cache_tex: glium::texture::Texture2d,
    font: Font<'a>,
}

impl<'a, T: RenderText<'a>> TextBuffer<'a, T> {
    pub fn new(display: Box<GlutinFacade>) -> Self {
        let dpi_factor = display.get_window().unwrap().hidpi_factor();

        let (cache_width, cache_height) = (512 * dpi_factor as u32, 512 * dpi_factor as u32);
        let cache = rusttype::gpu_cache::Cache::new(cache_width, cache_height, 0.1, 0.1);
        let cache_tex = glium::texture::Texture2d::with_format(
            &*display,
            glium::texture::RawImage2d {
                data: Cow::Owned(vec![0u8; cache_width as usize * cache_height as usize]),
                width: cache_width,
                height: cache_height,
                format: glium::texture::ClientFormat::U8
            },
            glium::texture::UncompressedFloatFormat::U8,
            glium::texture::MipmapsOption::NoMipmap).unwrap();

        TextBuffer {
            text_objects: None,
            vertices: Vec::new(),
            text_cache: cache,
            cache_tex: cache_tex,
            program: shaders::make_program_from_shaders(T::get_shaders(), &display),
            font: FontCollection::from_bytes(OPEN_SANS).into_font().unwrap(),
        }
    }

    fn load_text_into_cache<Text: RenderText<'a>>(
        &mut self,
        render_text: Text,
        glyph_scale: Scale)
    {
        let glyphs = layout_paragraph(&self.font, glyph_scale, &render_text.get_content());
        for glyph in &glyphs {
            self.text_cache.queue_glyph(0, glyph.clone());
        }

        let cache_tex = &mut self.cache_tex;
        let mut text_cache = &mut self.text_cache;
        text_cache.cache_queued(
            |rect, data| {
                cache_tex.main_level().write(glium::Rect {
                    left: rect.min.x,
                    bottom: rect.min.y,
                    width: rect.width(),
                    height: rect.height()
                }, glium::texture::RawImage2d {
                    data: Cow::Borrowed(data),
                    width: rect.width(),
                    height: rect.height(),
                    format: glium::texture::ClientFormat::U8
                });
            },
            1).unwrap();

        let glyph_pos_data: Vec<(Rect<f32>, Rect<i32>)> = glyphs
            .iter()
            .filter_map(|g| {
                if let Ok(Some(pos_data)) = text_cache.rect_for(0, g) {
                    Some(pos_data)
                } else {
                    None
                }}).collect();

        let mut vertices = render_text.get_vertices(glyph_pos_data, glyph_scale);
        self.vertices.append(&mut vertices);
    }

    fn render<Unif: glium::uniforms::Uniforms>(
        vertices: &Vec<TextVertex>,
        program: &glium::Program,
        target: &mut Frame,
        display: &GlutinFacade,
        _: &DrawParameters,
        uniforms: &Unif)
    {
        let vertex_buffer = glium::VertexBuffer::new(
            display,
            &vertices).unwrap();

        target.draw(&vertex_buffer,
                    glium::index::NoIndices(glium::index::PrimitiveType::Points),
                    &program,
                    uniforms,
                    &glium::DrawParameters {
                        blend: glium::Blend::alpha_blending(),
                        ..Default::default()
                    }).unwrap();

    }
}

impl<'a, T: RenderText<'a>> GliumBuffer<T> for TextBuffer<'a, T> {
    fn draw_at_target<Unif: glium::uniforms::Uniforms>(
        &mut self,
        target: &mut Frame,
        display: &GlutinFacade,
        view_details: view_details::ViewDetails,
        draw_params: &DrawParameters,
        _: &Unif
    ) {
        let buffer = self.text_objects.take();

        if let Some(buffer) = buffer {
            let (width, height) = target.get_dimensions();
            let dpi_factor = display.get_window().unwrap().hidpi_factor();

            let glyph_scale = Scale::uniform(256.0 * dpi_factor);

            for render_text in buffer {
                self.load_text_into_cache(render_text, glyph_scale);
            }

            let aspect_ratio = width as f64 / height as f64;

            let cache_tex = &self.cache_tex;
            let uniforms = uniform! {
                tex: cache_tex.sampled().magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
                screen_width: width,
                screen_height: height,
                aspect_ratio: aspect_ratio as f32,
                world_view: rendering::glium_renderer::GliumRenderer::create_worldview_mat(view_details, aspect_ratio)
            };

            Self::render(&self.vertices, &self.program, target, display, draw_params, &uniforms);
        }
    }

    fn load_renderable(&mut self, text: T) {
        if let Some(ref mut buffer) = self.text_objects {
            buffer.push(text);
        }
        else {
            self.text_objects = Some(vec![text]);
        }
    }

    fn flush_buffer(&mut self) {
        self.text_objects = None;
        self.vertices = Vec::new();
    }
}

fn layout_paragraph<'a>(font: &'a Font,
                        scale: Scale,
                        text: &str) -> Vec<PositionedGlyph<'a>> {
    use unicode_normalization::UnicodeNormalization;
    let mut result = Vec::new();
    let v_metrics = font.v_metrics(scale);
    let advance_height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap;
    let mut caret = point(0.0, v_metrics.ascent);
    let mut last_glyph_id = None;
    for c in text.nfc() {
        if c.is_control() {
            match c {
                '\r' => {
                    caret = point(0.0, caret.y + advance_height);
                }
                '\n' => {},
                _ => {}
            }
            continue;
        }
        let base_glyph = if let Some(glyph) = font.glyph(c) {
            glyph
        } else {
            continue;
        };
        if let Some(id) = last_glyph_id.take() {
            caret.x += font.pair_kerning(scale, id, base_glyph.id());
        }
        last_glyph_id = Some(base_glyph.id());
        let glyph = base_glyph.scaled(scale).positioned(caret);
        caret.x += glyph.unpositioned().h_metrics().advance_width;
        result.push(glyph);
    }
    result
}
<|endoftext|>


<commit_before>extern crate arguments;
extern crate configuration;
extern crate sql;
extern crate sqlite;
extern crate term;

#[macro_use] extern crate log;
#[macro_use] extern crate streamer;

use configuration::format::TOML;
use log::LogLevel;
use streamer::{Config, Result};
use streamer::{platform, schedule, traffic, workload};
use streamer::output::{self, Output};
use streamer::system::{self, Event};

mod logger;

use logger::Logger;

pub type System = system::System<traffic::Fractal,
                                 workload::Random,
                                 platform::Thermal,
                                 schedule::Impartial>;

const DEFAULT_LENGH: f64 = 10.0;

const USAGE: &'static str = "
Usage: streamer [options]

Options:
    --config <path>          Configuration file (required).
    --length <time>          Time span to synthesize in seconds [default: 10].
    --output <path>          Output file for power and temperature profiles.

    --verbose                Display progress information.
    --help                   Display this message.
";

#[allow(unused_must_use)]
fn main() {
    if let Err(error) = start() {
        use std::io::Write;
        if let Some(mut output) = term::stderr() {
            output.fg(term::color::RED);
            output.write_fmt(format_args!("Error: {}.\n", error));
            output.reset();
        }
        std::process::exit(1);
    }
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));
    if arguments.get::<bool>("help").unwrap_or(false) {
        println!("{}", USAGE.trim());
        return Ok(());
    }
    if arguments.get::<bool>("verbose").unwrap_or(false) {
        Logger::install(LogLevel::Info);
    } else {
        Logger::install(LogLevel::Warn);
    }

    let config = ok!(TOML::open(some!(arguments.get::<String>("config"),
                                      "a configuration file is required")));

    let mut system = try!(setup(&config));
    let mut output = match arguments.get::<String>("output") {
        Some(path) => Some(try!(output::Thermal::new(system.platform(), path))),
        _ => None,
    };

    let length = arguments.get::<f64>("length").unwrap_or(DEFAULT_LENGH);

    info!(target: "Streamer", "Synthesizing {} seconds...", length);
    while let Some((event, data)) = try!(system.next()) {
        display(&system, &event);
        if let Some(ref mut output) = output {
            try!(output.next(&event, &data));
        }
        if event.time > length {
            break;
        }
    }
    info!(target: "Streamer", "Well done.");

    Ok(())
}

fn display(system: &System, event: &Event) {
    use streamer::system::EventKind;

    let (job, kind) = match &event.kind {
        &EventKind::Arrived(ref job) => (job, "arrived"),
        &EventKind::Started(ref job) => (job, "started"),
        &EventKind::Finished(ref job) => (job, "finished"),
    };
    info!(target: "Streamer",
          "{:8.2} s | job #{:4} ( {:20} | {:2} units | {:6.2} s ) {:8} | {:2} queued",
          event.time, job.id, job.name, job.units, job.duration(), kind,
          system.history().arrived - system.history().started);
}

fn setup(config: &Config) -> Result<System> {
    macro_rules! branch(($name:expr) => (config.branch($name).as_ref().unwrap_or(config)));

    let source = streamer::source(config);
    let traffic = try!(traffic::Fractal::new(branch!("traffic"), &source));
    let workload = try!(workload::Random::new(branch!("workload"), &source));
    let platform = try!(platform::Thermal::new(branch!("platform")));
    let schedule = try!(schedule::Impartial::new(branch!("schedule"), &platform));

    System::new(traffic, workload, platform, schedule)
}
<commit_msg>bin: inline the setup function<commit_after>extern crate arguments;
extern crate configuration;
extern crate sql;
extern crate sqlite;
extern crate term;

#[macro_use] extern crate log;
#[macro_use] extern crate streamer;

use configuration::format::TOML;
use log::LogLevel;
use streamer::{Result, platform, schedule, traffic, workload};
use streamer::output::{self, Output};
use streamer::system::{self, Event};

mod logger;

use logger::Logger;

type System = system::System<traffic::Fractal,
                             workload::Random,
                             platform::Thermal,
                             schedule::Impartial>;

const USAGE: &'static str = "
Usage: streamer [options]

Options:
    --config <path>          Configuration file (required).
    --length <time>          Time span to synthesize in seconds [default: 10].
    --output <path>          Output file for power and temperature profiles.

    --verbose                Display progress information.
    --help                   Display this message.
";

#[allow(unused_must_use)]
fn main() {
    if let Err(error) = start() {
        use std::io::Write;
        if let Some(mut output) = term::stderr() {
            output.fg(term::color::RED);
            output.write_fmt(format_args!("Error: {}.\n", error));
            output.reset();
        }
        std::process::exit(1);
    }
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));
    if arguments.get::<bool>("help").unwrap_or(false) {
        println!("{}", USAGE.trim());
        return Ok(());
    }
    if arguments.get::<bool>("verbose").unwrap_or(false) {
        Logger::install(LogLevel::Info);
    } else {
        Logger::install(LogLevel::Warn);
    }

    let mut system = {
        let config = ok!(TOML::open(some!(arguments.get::<String>("config"),
                                          "a configuration file is required")));
        macro_rules! branch(($name:expr) => (config.branch($name).as_ref().unwrap_or(&config)));
        let source = streamer::source(&config);
        let traffic = try!(traffic::Fractal::new(branch!("traffic"), &source));
        let workload = try!(workload::Random::new(branch!("workload"), &source));
        let platform = try!(platform::Thermal::new(branch!("platform")));
        let schedule = try!(schedule::Impartial::new(branch!("schedule"), &platform));
        try!(System::new(traffic, workload, platform, schedule))
    };
    let mut output = match arguments.get::<String>("output") {
        Some(path) => Some(try!(output::Thermal::new(system.platform(), path))),
        _ => None,
    };

    let length = arguments.get::<f64>("length").unwrap_or(10.0);
    info!(target: "Streamer", "Synthesizing {} seconds...", length);
    while let Some((event, data)) = try!(system.next()) {
        display(&system, &event);
        if let Some(ref mut output) = output {
            try!(output.next(&event, &data));
        }
        if event.time > length {
            break;
        }
    }
    info!(target: "Streamer", "Well done.");

    Ok(())
}

fn display(system: &System, event: &Event) {
    use streamer::system::EventKind;

    let (job, kind) = match &event.kind {
        &EventKind::Arrived(ref job) => (job, "arrived"),
        &EventKind::Started(ref job) => (job, "started"),
        &EventKind::Finished(ref job) => (job, "finished"),
    };
    info!(target: "Streamer",
          "{:8.2} s | job #{:4} ( {:20} | {:2} units | {:6.2} s ) {:8} | {:2} queued",
          event.time, job.id, job.name, job.units, job.duration(), kind,
          system.history().arrived - system.history().started);
}
<|endoftext|>


<commit_before>#![feature(test)]
#![allow(unused_imports)]
#![allow(dead_code)]

extern crate test;

#[macro_use]
extern crate teleport;

use std::fmt::Write;
use std::mem::replace;
use test::Bencher;
use std::iter::{repeat, FromIterator, IntoIterator};

use teleport::{push_attribute, AttributeValue, Attribute};

#[bench]
fn string_concat(b: &mut Bencher) {

   let mut table_contents: Vec<Vec<u32>> = Vec::new();
   //let mut table_contents: Vec<[u32; 10]> = Vec::new();
   for i in 0..10000 {
      let k = i*10;
      table_contents.push(Vec::from_iter((k..k+10)))
      //table_contents.push([k, k+1, k+2, k+3, k+4, k+5, k+6, k+7, k+8, k+9]);
   }

   b.iter(|| {
      let mut result = String::new();
      result.push_str("
\n");

      for index in 0..10000 {//table_contents.iter() {
         result.push_str("
\n");

         for item in table_contents[index].iter() {
            result.push_str("    ");
            result.push_str(&item.to_string());
            result.push_str("\n");
         }

         result.push_str("
\n");
      }

      result.push_str("
");
   });
}

pub fn main() {
   let class = AttributeValue::List(vec![
      AttributeValue::StaticString("first"),
      AttributeValue::StaticString("second")
   ]);

   let first_attr = Attribute {
      name: "class",
      value: class,
   };

   let mut rendered = String::new();

   push_attribute(&first_attr, &mut rendered);

   println!("{}", rendered);

   let list = attr!["id", "location"; "class", "identical"];

   println!("{:?}", list);
}
<commit_msg>More template speed benchmarking.<commit_after>#![feature(test)]
#![allow(unused_imports)]
#![allow(dead_code)]

extern crate test;

#[macro_use]
extern crate teleport;

use std::fmt::Write;
use std::mem::replace;
use test::Bencher;
use std::iter::{repeat, FromIterator, IntoIterator};

use teleport::{push_attribute, AttributeValue, Attribute};

#[bench]
fn concat_string_source(b: &mut Bencher) {
   let mut table_contents: Vec<[&'static str; 10]> = Vec::new();
   for _ in 0..1000 {
      table_contents.push(["aa", "bb", "cc", "dd", "ee", "ff", "gg", "hh", "ii", "jj"])
   }

   b.iter(|| {
      let mut result = String::with_capacity(200000);
      result.push_str("
\n");

      for row in table_contents.iter() {
         result.push_str("
\n");

         for item in row {
            result.push_str("    ");
            result.push_str(&item.to_string());
            result.push_str("\n");
         }

         result.push_str("
\n");
      }

      result.push_str("
");

      result
   });
}

#[bench]
fn concat_vector_source(b: &mut Bencher) {
   let mut table_contents: Vec<[&'static str; 10]> = Vec::new();
   for _ in 0..1000 {
      table_contents.push(["aa", "bb", "cc", "dd", "ee", "ff", "gg", "hh", "ii", "jj"])
   }

   b.iter(|| {
      let mut result = Vec::new();
      result.push("
\n");

      for row in table_contents.iter() {
         result.push("
\n");

         for item in row {
            result.push("    ");
            result.push(item);
            result.push("\n");
         }

         result.push("
\n");
      }

      result.push("
");
   });
}

fn render_table() {
   let mut table_contents: Vec<[&'static str; 10]> = Vec::new();
   for _ in 0..1000 {
      table_contents.push(["aa", "bb", "cc", "dd", "ee", "ff", "gg", "hh", "ii", "jj"])
   }

   let mut result = Vec::with_capacity(36000);
   result.push("
\n");

   for row in table_contents.iter() {
      result.push("
\n");

      for item in row {
         result.push("    ");
         result.push(item);
         result.push("\n");
      }

      result.push("
\n");
   }

   result.push("
");

   println!("{}", result.len())
}

pub fn main() {
   render_table();

   // let class = AttributeValue::List(vec![
   //    AttributeValue::StaticString("first"),
   //    AttributeValue::StaticString("second")
   // ]);
   //
   // let first_attr = Attribute {
   //    name: "class",
   //    value: class,
   // };
   //
   // let mut rendered = String::new();
   //
   // push_attribute(&first_attr, &mut rendered);
   //
   // println!("{}", rendered);
   //
   // let list = attr!["id", "location"; "class", "identical"];
   //
   // println!("{:?}", list);
}
<|endoftext|>


<commit_before>// program args
use std::env;

// io/fs ops
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::{File, OpenOptions};
use std::path::Path;

// a double linked list
use std::collections::LinkedList;
use std::iter::FromIterator;

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

#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
enum TriLogic {
    True      = 0b01,
    False     = 0b10,
    DontCare  = 0b11
}

type CubeList =  LinkedList<Vec<TriLogic>>;

fn cubelist_contains_all_dont_cares_cube(F: &CubeList) -> bool {
    // `any` of the cubes contains `all` dont cares
    F.iter().any(|c: &Vec<TriLogic>| ((*c).iter().all(|t: &TriLogic| *t == TriLogic::DontCare )))
}

#[test]
fn test_contains_all_dont_cares_cube() {
    assert!(!cubelist_contains_all_dont_cares_cube(
            &LinkedList::from_iter(vec![])),
            "case0: empty cubelist");
    assert!(!cubelist_contains_all_dont_cares_cube(
            &LinkedList::from_iter(vec![vec![TriLogic::True, TriLogic::DontCare, TriLogic::DontCare]])),
            "case1: [[True, DontCare, DontCare]]");
    assert!( cubelist_contains_all_dont_cares_cube(
            &LinkedList::from_iter(vec![vec![TriLogic::DontCare, TriLogic::DontCare, TriLogic::DontCare],
                                        vec![TriLogic::True,     TriLogic::False,    TriLogic::DontCare]])),
            "case2: [[DontCare, ...]]");
}

fn count_all_dont_cares(cube: &[TriLogic]) -> usize {
    (*cube).iter().filter(|term| **term == TriLogic::DontCare).count()
}

fn count_all_not_dont_cares(cube: &[TriLogic]) -> usize {
    (*cube).iter().filter(|term| **term != TriLogic::DontCare).count()
}

#[test]
fn test_count_don_cares() {
    let cube = [TriLogic::True,     TriLogic::False,
                TriLogic::DontCare, TriLogic::DontCare,
                TriLogic::False,    TriLogic::DontCare, TriLogic::True];
    assert!(count_all_dont_cares(&cube) == 3, "count all dont_cares");
    assert!(count_all_not_dont_cares(&cube) == 4, "count all not dont_cares");
}

fn most_binate_variable(F: &CubeList) -> usize {

    #[derive(Clone, PartialEq, PartialOrd, Eq, Debug)]
    struct BinateVarAttrs(isize, isize, isize); // True, Complement, Index

    #[inline]
    fn cmp_binate(left: &BinateVarAttrs, right: &BinateVarAttrs) -> Ordering {
        match (left, right) {
            (&BinateVarAttrs(t1, c1, i1), &BinateVarAttrs(t2, c2, i2)) =>
                if      t2+c2 < t1+c1 { Ordering::Less }
                else if t2+c2 > t1+c1 { Ordering::Greater }
                else { // t1+c1 == t2+c2
                    if      (t1-c1).abs() < (t1-c1).abs() { Ordering::Less }
                    else if (t1-c1).abs() > (t1-c1).abs() { Ordering::Greater }
                    else if i1 < i2 { Ordering::Less }
                    else { Ordering::Equal }
                }

        }

    }

    #[inline]
    fn cmp_unate(left: &BinateVarAttrs, right: &BinateVarAttrs) -> Ordering {
        match (left, right) {
            (&BinateVarAttrs(t1, c1, i1), &BinateVarAttrs(t2, c2, i2)) =>
                if      t2+c2 < t1+c1 { Ordering::Less }
                else if t2+c2 > t1+c1 { Ordering::Greater }
                else { // t1+c1 == t2+c2
                    if i1 < i2 { Ordering::Less }
                    else { Ordering::Equal }
                }

        }

    }

    let num_cubes = F.len();
    let num_vars = if num_cubes >= 1 { F.front().unwrap().len() } else { 0 };
    let mut tie = vec![BinateVarAttrs(0, 0, 0); num_vars];
    for c in F.iter() {
        for i in 0 .. (*c).len() {
            let term = (*c)[i].clone();
            let j = i as isize;
            if        term == TriLogic::True {
                tie[i] = match tie[i] { BinateVarAttrs(t,c,_) => BinateVarAttrs(t+1,c,j) }
            } else if term == TriLogic::False {
                tie[i] = match tie[i] { BinateVarAttrs(t,c,_) => BinateVarAttrs(t,c+1,j) }
            } else {
                tie[i] = match tie[i] { BinateVarAttrs(t,c,_) => BinateVarAttrs(t,c,j) }
            }
        }
    }

    for k in 0..num_vars {
        let BinateVarAttrs(t,c,i) = tie[k];
        println!("tie[{}] = ({},{},{})",k,t,c,i);
    }

    let there_are_binate_vars = tie.iter()
        .any(|x| match *x { BinateVarAttrs(t,c,_) => t > 0 && c > 0 } );
    let split_var_idx = if there_are_binate_vars {
        // filter tie, leave only binate cubes
        let mut tie_binate: Vec<BinateVarAttrs> = tie.iter()
            .filter(|&x| match *x { BinateVarAttrs(t,c,_)  => t > 0 && c > 0  } )
            .map(|x| x.clone()).collect();
        tie_binate.sort_by(cmp_binate);
        println!("sorted binate: {:?}", tie_binate);
        let BinateVarAttrs(_, _, split_var_idx) = tie_binate[0];
        split_var_idx
    } else {
        tie.sort_by(cmp_unate);
        let BinateVarAttrs(_, _, split_var_idx) = tie[0];
        split_var_idx
    };
    println!("split_var_idx = {}", split_var_idx);
    split_var_idx as usize
}

fn positiveCofactor(F: &CubeList, x: usize) -> CubeList {
    let mut cofactor_list: CubeList = LinkedList::new();
    for c in F.iter() {
        let mut cube = c.clone();
        let term_val: TriLogic = cube[x].clone();
        match term_val {
            TriLogic::True      => {  cube[x] = TriLogic::DontCare;
                                      cofactor_list.push_back(cube) }
            TriLogic::False     => {} // => remove this cube
            TriLogic::DontCare  => {  cofactor_list.push_back(cube) } // leave it as is
        }
    }
    cofactor_list
}

fn negativeCofactor(F: &CubeList, x: usize) -> CubeList {
    let mut cofactor_list: CubeList = LinkedList::new();
    for c in F.iter() {
        let mut cube = c.clone();
        let term_val: TriLogic = cube[x].clone();
        match term_val {
            TriLogic::True      => {} // remove this cube
            TriLogic::False     => {  cube[x] = TriLogic::DontCare;
                                      cofactor_list.push_back(cube) }
            TriLogic::DontCare  => {  cofactor_list.push_back(cube) } // leave it as is
        }
    }
    cofactor_list
}

#[test]
fn cofactors_test() {
    let clist: CubeList = LinkedList::from_iter(
        vec![vec![TriLogic::True,     TriLogic::True, TriLogic::DontCare, TriLogic::True],
             vec![TriLogic::DontCare, TriLogic::True, TriLogic::False,    TriLogic::DontCare]]);

    let mut fa = positiveCofactor(&clist, 0);
    let fc = positiveCofactor(&clist, 2);

    assert!(fa.len() == 2);
    assert!(fc.len() == 1);
    assert!(fa.front().unwrap() ==    &[TriLogic::DontCare, TriLogic::True, TriLogic::DontCare, TriLogic::True],      "fa -- 1st cube");
    assert!(fa.pop_back() == Some( vec![TriLogic::DontCare, TriLogic::True, TriLogic::False,    TriLogic::DontCare]), "fa -- 2nd cube");
    assert!(fc.front().unwrap() ==    &[TriLogic::True,     TriLogic::True, TriLogic::DontCare, TriLogic::True],      "fc -- 1st cube");
}

// x offset is 1 to distinguish positive x and negative x'
fn AND(x: isize, F: &CubeList) -> CubeList {
    let ind = (x.abs() - 1) as usize;
    // check that cubelist lacks variable x
    assert!(F.iter().all(|c| c[ind] == TriLogic::DontCare ) );
    let mut and_list: CubeList = LinkedList::new();
    for c in F.iter() {
        let mut cube = c.clone();
        cube[ind] = if x > 0 { TriLogic::True } else { TriLogic::False };
        and_list.push_back(cube)
    }
    and_list
}

#[test]
fn AND_test() {
    let clist: CubeList = LinkedList::from_iter(
        vec![vec![TriLogic::DontCare, TriLogic::True,     TriLogic::True, TriLogic::DontCare],
             vec![TriLogic::DontCare, TriLogic::DontCare, TriLogic::True, TriLogic::False]]);
    let mut anda = AND(1, &clist);
    assert!(anda.len() == 2, "and_test list length check");
    assert!(anda.front().unwrap() ==    &[TriLogic::True, TriLogic::True,     TriLogic::True, TriLogic::DontCare], "and_test 1st cube");
    assert!(anda.pop_back() == Some( vec![TriLogic::True, TriLogic::DontCare, TriLogic::True, TriLogic::False]),   "and_test 2nd cube");
}

fn OR(P: &CubeList, N: &CubeList) -> CubeList {
    let mut or_list = P.clone();
    or_list.append(&mut N.clone());
    or_list
}

#[test]
fn OR_test() {
    let clist1: CubeList = LinkedList::from_iter(
        vec![vec![TriLogic::DontCare, TriLogic::True,     TriLogic::True, TriLogic::DontCare],
             vec![TriLogic::DontCare, TriLogic::DontCare, TriLogic::True, TriLogic::False]]);
    let clist2: CubeList = LinkedList::from_iter(
        vec![vec![TriLogic::True,     TriLogic::False,    TriLogic::True, TriLogic::DontCare],
             vec![TriLogic::DontCare, TriLogic::DontCare, TriLogic::True, TriLogic::False]]);
    let or_list = OR(&clist1, &clist2);
    assert!(or_list.len() == 4, "or_test list length check");
}

fn direct_complement(F: &CubeList) -> CubeList {
    F.clone()
}

fn Complement(num_vars: usize, F: &CubeList) -> CubeList {
    let F_is_simple = || -> (bool, CubeList) {
        if F.len() == 0 { // empty cube list
            let mut clist: CubeList = LinkedList::new();
            clist.push_back(vec![TriLogic::DontCare; num_vars]);
            (true, clist)
        } else if cubelist_contains_all_dont_cares_cube(F) {
            (true, LinkedList::new())
        } else if F.len() == 1 { // cube list contains exactly one cube
            (true, direct_complement(F))
        } else {
            (false, LinkedList::new())
        }
    };

    let x = most_binate_variable(F);
    F.clone()
}

fn main()
{
    let args: Vec<String> = env::args().collect();
    if args.len() < 3 {
        println!("plz. gimme input and output fnames");
        return;
    }

    let inp_fname = args[1].clone();
    let out_fname = args[2].clone();
    let is = File::open(inp_fname).unwrap(); // TODO try with `try!`
    let reader = BufReader::new(is);
    let mut line_num = 0;
    let mut num_vars:  usize = 0;
    let mut num_cubes: usize = 0;
    let mut cube_list: Vec<Vec<TriLogic>> = Vec::new();

    for maybe_line in reader.lines() {
        let line = maybe_line.unwrap();
        line_num += 1;
        println!("<{}> : {}", line_num, line);
        let chunks: Vec<&str> = line.split_terminator(|c: char| c.is_whitespace()).collect();
        match line_num {
            1 => num_vars  = chunks[0].parse().ok().expect("a number expected"),
            2 =>
            {
                num_cubes  = chunks[0].parse().ok().expect("a number expected");
                println!("num_vars: {} num_cubes: {}", num_vars, num_cubes);
            },
            _ =>
            {
                if (line_num - 2) > num_cubes { break }
                let nterms: usize  = chunks[0].parse().ok().expect("a number expected");
                let terms: Vec<isize> = Vec::from_iter ((0..nterms)
                                            .map (|idx| chunks[idx+1].parse().ok()
                                                  .expect("a number expected")));
                println!("nterms: {} terms: {:?}", nterms, terms);
                let mut cube = vec![TriLogic::DontCare; num_vars];
                for term in terms.iter() {
                    if *term > 0 {
                        cube[( *term - 1) as usize] = TriLogic::True;
                    } else {
                        cube[(-*term - 1) as usize] = TriLogic::False;
                    }
                }
                cube_list.push(cube);
            }
        }
    }
    println!("cube_list: {:?}", cube_list);
    let compl = Complement(num_vars, &LinkedList::from_iter(cube_list));
}
<commit_msg>added a license to `binary_unate`.<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Alexander Samoilov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// program args
use std::env;

// io/fs ops
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::{File, OpenOptions};
use std::path::Path;

// a double linked list
use std::collections::LinkedList;
use std::iter::FromIterator;

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

#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
enum TriLogic {
    True      = 0b01,
    False     = 0b10,
    DontCare  = 0b11
}

type CubeList =  LinkedList<Vec<TriLogic>>;

fn cubelist_contains_all_dont_cares_cube(F: &CubeList) -> bool {
    // `any` of the cubes contains `all` dont cares
    F.iter().any(|c: &Vec<TriLogic>| ((*c).iter().all(|t: &TriLogic| *t == TriLogic::DontCare )))
}

#[test]
fn test_contains_all_dont_cares_cube() {
    assert!(!cubelist_contains_all_dont_cares_cube(
            &LinkedList::from_iter(vec![])),
            "case0: empty cubelist");
    assert!(!cubelist_contains_all_dont_cares_cube(
            &LinkedList::from_iter(vec![vec![TriLogic::True, TriLogic::DontCare, TriLogic::DontCare]])),
            "case1: [[True, DontCare, DontCare]]");
    assert!( cubelist_contains_all_dont_cares_cube(
            &LinkedList::from_iter(vec![vec![TriLogic::DontCare, TriLogic::DontCare, TriLogic::DontCare],
                                        vec![TriLogic::True,     TriLogic::False,    TriLogic::DontCare]])),
            "case2: [[DontCare, ...]]");
}

fn count_all_dont_cares(cube: &[TriLogic]) -> usize {
    (*cube).iter().filter(|term| **term == TriLogic::DontCare).count()
}

fn count_all_not_dont_cares(cube: &[TriLogic]) -> usize {
    (*cube).iter().filter(|term| **term != TriLogic::DontCare).count()
}

#[test]
fn test_count_don_cares() {
    let cube = [TriLogic::True,     TriLogic::False,
                TriLogic::DontCare, TriLogic::DontCare,
                TriLogic::False,    TriLogic::DontCare, TriLogic::True];
    assert!(count_all_dont_cares(&cube) == 3, "count all dont_cares");
    assert!(count_all_not_dont_cares(&cube) == 4, "count all not dont_cares");
}

fn most_binate_variable(F: &CubeList) -> usize {

    #[derive(Clone, PartialEq, PartialOrd, Eq, Debug)]
    struct BinateVarAttrs(isize, isize, isize); // True, Complement, Index

    #[inline]
    fn cmp_binate(left: &BinateVarAttrs, right: &BinateVarAttrs) -> Ordering {
        match (left, right) {
            (&BinateVarAttrs(t1, c1, i1), &BinateVarAttrs(t2, c2, i2)) =>
                if      t2+c2 < t1+c1 { Ordering::Less }
                else if t2+c2 > t1+c1 { Ordering::Greater }
                else { // t1+c1 == t2+c2
                    if      (t1-c1).abs() < (t1-c1).abs() { Ordering::Less }
                    else if (t1-c1).abs() > (t1-c1).abs() { Ordering::Greater }
                    else if i1 < i2 { Ordering::Less }
                    else { Ordering::Equal }
                }

        }

    }

    #[inline]
    fn cmp_unate(left: &BinateVarAttrs, right: &BinateVarAttrs) -> Ordering {
        match (left, right) {
            (&BinateVarAttrs(t1, c1, i1), &BinateVarAttrs(t2, c2, i2)) =>
                if      t2+c2 < t1+c1 { Ordering::Less }
                else if t2+c2 > t1+c1 { Ordering::Greater }
                else { // t1+c1 == t2+c2
                    if i1 < i2 { Ordering::Less }
                    else { Ordering::Equal }
                }

        }

    }

    let num_cubes = F.len();
    let num_vars = if num_cubes >= 1 { F.front().unwrap().len() } else { 0 };
    let mut tie = vec![BinateVarAttrs(0, 0, 0); num_vars];
    for c in F.iter() {
        for i in 0 .. (*c).len() {
            let term = (*c)[i].clone();
            let j = i as isize;
            if        term == TriLogic::True {
                tie[i] = match tie[i] { BinateVarAttrs(t,c,_) => BinateVarAttrs(t+1,c,j) }
            } else if term == TriLogic::False {
                tie[i] = match tie[i] { BinateVarAttrs(t,c,_) => BinateVarAttrs(t,c+1,j) }
            } else {
                tie[i] = match tie[i] { BinateVarAttrs(t,c,_) => BinateVarAttrs(t,c,j) }
            }
        }
    }

    for k in 0..num_vars {
        let BinateVarAttrs(t,c,i) = tie[k];
        println!("tie[{}] = ({},{},{})",k,t,c,i);
    }

    let there_are_binate_vars = tie.iter()
        .any(|x| match *x { BinateVarAttrs(t,c,_) => t > 0 && c > 0 } );
    let split_var_idx = if there_are_binate_vars {
        // filter tie, leave only binate cubes
        let mut tie_binate: Vec<BinateVarAttrs> = tie.iter()
            .filter(|&x| match *x { BinateVarAttrs(t,c,_)  => t > 0 && c > 0  } )
            .map(|x| x.clone()).collect();
        tie_binate.sort_by(cmp_binate);
        println!("sorted binate: {:?}", tie_binate);
        let BinateVarAttrs(_, _, split_var_idx) = tie_binate[0];
        split_var_idx
    } else {
        tie.sort_by(cmp_unate);
        let BinateVarAttrs(_, _, split_var_idx) = tie[0];
        split_var_idx
    };
    println!("split_var_idx = {}", split_var_idx);
    split_var_idx as usize
}

fn positiveCofactor(F: &CubeList, x: usize) -> CubeList {
    let mut cofactor_list: CubeList = LinkedList::new();
    for c in F.iter() {
        let mut cube = c.clone();
        let term_val: TriLogic = cube[x].clone();
        match term_val {
            TriLogic::True      => {  cube[x] = TriLogic::DontCare;
                                      cofactor_list.push_back(cube) }
            TriLogic::False     => {} // => remove this cube
            TriLogic::DontCare  => {  cofactor_list.push_back(cube) } // leave it as is
        }
    }
    cofactor_list
}

fn negativeCofactor(F: &CubeList, x: usize) -> CubeList {
    let mut cofactor_list: CubeList = LinkedList::new();
    for c in F.iter() {
        let mut cube = c.clone();
        let term_val: TriLogic = cube[x].clone();
        match term_val {
            TriLogic::True      => {} // remove this cube
            TriLogic::False     => {  cube[x] = TriLogic::DontCare;
                                      cofactor_list.push_back(cube) }
            TriLogic::DontCare  => {  cofactor_list.push_back(cube) } // leave it as is
        }
    }
    cofactor_list
}

#[test]
fn cofactors_test() {
    let clist: CubeList = LinkedList::from_iter(
        vec![vec![TriLogic::True,     TriLogic::True, TriLogic::DontCare, TriLogic::True],
             vec![TriLogic::DontCare, TriLogic::True, TriLogic::False,    TriLogic::DontCare]]);

    let mut fa = positiveCofactor(&clist, 0);
    let fc = positiveCofactor(&clist, 2);

    assert!(fa.len() == 2);
    assert!(fc.len() == 1);
    assert!(fa.front().unwrap() ==    &[TriLogic::DontCare, TriLogic::True, TriLogic::DontCare, TriLogic::True],      "fa -- 1st cube");
    assert!(fa.pop_back() == Some( vec![TriLogic::DontCare, TriLogic::True, TriLogic::False,    TriLogic::DontCare]), "fa -- 2nd cube");
    assert!(fc.front().unwrap() ==    &[TriLogic::True,     TriLogic::True, TriLogic::DontCare, TriLogic::True],      "fc -- 1st cube");
}

// x offset is 1 to distinguish positive x and negative x'
fn AND(x: isize, F: &CubeList) -> CubeList {
    let ind = (x.abs() - 1) as usize;
    // check that cubelist lacks variable x
    assert!(F.iter().all(|c| c[ind] == TriLogic::DontCare ) );
    let mut and_list: CubeList = LinkedList::new();
    for c in F.iter() {
        let mut cube = c.clone();
        cube[ind] = if x > 0 { TriLogic::True } else { TriLogic::False };
        and_list.push_back(cube)
    }
    and_list
}

#[test]
fn AND_test() {
    let clist: CubeList = LinkedList::from_iter(
        vec![vec![TriLogic::DontCare, TriLogic::True,     TriLogic::True, TriLogic::DontCare],
             vec![TriLogic::DontCare, TriLogic::DontCare, TriLogic::True, TriLogic::False]]);
    let mut anda = AND(1, &clist);
    assert!(anda.len() == 2, "and_test list length check");
    assert!(anda.front().unwrap() ==    &[TriLogic::True, TriLogic::True,     TriLogic::True, TriLogic::DontCare], "and_test 1st cube");
    assert!(anda.pop_back() == Some( vec![TriLogic::True, TriLogic::DontCare, TriLogic::True, TriLogic::False]),   "and_test 2nd cube");
}

fn OR(P: &CubeList, N: &CubeList) -> CubeList {
    let mut or_list = P.clone();
    or_list.append(&mut N.clone());
    or_list
}

#[test]
fn OR_test() {
    let clist1: CubeList = LinkedList::from_iter(
        vec![vec![TriLogic::DontCare, TriLogic::True,     TriLogic::True, TriLogic::DontCare],
             vec![TriLogic::DontCare, TriLogic::DontCare, TriLogic::True, TriLogic::False]]);
    let clist2: CubeList = LinkedList::from_iter(
        vec![vec![TriLogic::True,     TriLogic::False,    TriLogic::True, TriLogic::DontCare],
             vec![TriLogic::DontCare, TriLogic::DontCare, TriLogic::True, TriLogic::False]]);
    let or_list = OR(&clist1, &clist2);
    assert!(or_list.len() == 4, "or_test list length check");
}

fn direct_complement(F: &CubeList) -> CubeList {
    F.clone()
}

fn Complement(num_vars: usize, F: &CubeList) -> CubeList {
    let F_is_simple = || -> (bool, CubeList) {
        if F.len() == 0 { // empty cube list
            let mut clist: CubeList = LinkedList::new();
            clist.push_back(vec![TriLogic::DontCare; num_vars]);
            (true, clist)
        } else if cubelist_contains_all_dont_cares_cube(F) {
            (true, LinkedList::new())
        } else if F.len() == 1 { // cube list contains exactly one cube
            (true, direct_complement(F))
        } else {
            (false, LinkedList::new())
        }
    };

    let x = most_binate_variable(F);
    F.clone()
}

fn main()
{
    let args: Vec<String> = env::args().collect();
    if args.len() < 3 {
        println!("plz. gimme input and output fnames");
        return;
    }

    let inp_fname = args[1].clone();
    let out_fname = args[2].clone();
    let is = File::open(inp_fname).unwrap(); // TODO try with `try!`
    let reader = BufReader::new(is);
    let mut line_num = 0;
    let mut num_vars:  usize = 0;
    let mut num_cubes: usize = 0;
    let mut cube_list: Vec<Vec<TriLogic>> = Vec::new();

    for maybe_line in reader.lines() {
        let line = maybe_line.unwrap();
        line_num += 1;
        println!("<{}> : {}", line_num, line);
        let chunks: Vec<&str> = line.split_terminator(|c: char| c.is_whitespace()).collect();
        match line_num {
            1 => num_vars  = chunks[0].parse().ok().expect("a number expected"),
            2 =>
            {
                num_cubes  = chunks[0].parse().ok().expect("a number expected");
                println!("num_vars: {} num_cubes: {}", num_vars, num_cubes);
            },
            _ =>
            {
                if (line_num - 2) > num_cubes { break }
                let nterms: usize  = chunks[0].parse().ok().expect("a number expected");
                let terms: Vec<isize> = Vec::from_iter ((0..nterms)
                                            .map (|idx| chunks[idx+1].parse().ok()
                                                  .expect("a number expected")));
                println!("nterms: {} terms: {:?}", nterms, terms);
                let mut cube = vec![TriLogic::DontCare; num_vars];
                for term in terms.iter() {
                    if *term > 0 {
                        cube[( *term - 1) as usize] = TriLogic::True;
                    } else {
                        cube[(-*term - 1) as usize] = TriLogic::False;
                    }
                }
                cube_list.push(cube);
            }
        }
    }
    println!("cube_list: {:?}", cube_list);
    let compl = Complement(num_vars, &LinkedList::from_iter(cube_list));
}
<|endoftext|>


<commit_before>use search_algorithms::board::ExtendedBoard;
use search_algorithms::board::GameResult;
use search_algorithms::alpha_beta::Score;
use pgn::PgnBoard;
use rayon::prelude::*;
use std::fmt::Debug;

pub trait TunableBoard {
    const PARAMS: &'static [f32];

    fn static_eval_with_params(&self, params: &[f32]) -> f32;
}

pub fn get_critical_positions<B>(positions: &[B], params: &[f32]) -> Vec<B>
    where B: TunableBoard + ExtendedBoard + PgnBoard + Send + Sync {
    positions.into_par_iter()
        .map(|board| {
            if let (Score::Val(_), pv) = qsearch(&mut board.clone(), Score::Loss(0),
                                     Score::Win(0), params) {
                let mut cloned_board = board.clone();

                for mv in pv.into_iter().rev() {
                    cloned_board.do_move(mv);
                }
                cloned_board
            }
            else {
                warn!("Found mate score while tuning {}.", board.to_fen());
                board.clone()
            }

        })
        .collect()
}

pub fn gradient_descent<B>(positions: &[B], results: &[GameResult],
                           test_positions: &[B], test_results: &[GameResult],
                           params: &[f32]) -> Vec<f32>
    where B: TunableBoard + ExtendedBoard + PgnBoard + Send + Debug + Sync {
    assert_eq!(positions.len(), results.len());
    assert_eq!(test_positions.len(), test_results.len());

    let mut eta = 0.1;
    let beta = 0.8;

    let initial_error = average_error(test_positions, test_results, params);
    println!("Initial error: {}", initial_error);

    let mut errors = vec![initial_error];
    let mut lowest_error = initial_error;
    let mut paramss: Vec<Vec<f32>> = vec![params.to_vec()];
    let mut best_params = params.to_vec();
    let mut gradients = vec![0.0; params.len()];

    loop {
        let last_params = paramss.last().unwrap().clone();
        let slopes = calc_slope(positions, results, &last_params);
        gradients = gradients.iter()
            .zip(slopes)
            .map(|(gradient, slope)| beta * gradient + (1.0 - beta) * slope)
            .collect();
        println!("Gradients: {:?}", gradients);

        let new_params: Vec<f32> = last_params.iter()
            .zip(gradients.iter())
            .map(|(param, gradient)| param + gradient * eta)
            .collect();
        println!("New parameters: {:?}", new_params);

        let error = average_error(test_positions, test_results, &new_params);
        println!("Error now {}\n", error);

        if error < lowest_error {
            lowest_error = error;
            best_params = new_params.to_vec();
        }
        else if errors.len() > 3
            && error > lowest_error
            && errors[errors.len() - 1] > lowest_error
            && errors[errors.len() - 2] > lowest_error
            && errors[errors.len() - 3] > lowest_error
            && errors[errors.len() - 4] > lowest_error
        {
            if eta < 0.005 {
                return best_params
            }
            else {
                eta = eta / 10.0;
                paramss = vec![best_params.clone()];
                errors = vec![lowest_error];
                println!("Reduced eta to {}\n", eta);
                continue;
            }
        }
        errors.push(error);
        paramss.push(new_params);
    }
}

pub fn calc_slope<B>(positions: &[B], results: &[GameResult], params: &[f32]) -> Vec<f32>
    where B: TunableBoard + ExtendedBoard + PgnBoard + Send + Sync {

    let critical_positions = get_critical_positions(positions, params);

    const EPSILON : f32 = 0.001;

    params.par_iter().enumerate()
        .map(|(i, p)| {
            let mut params_hat: Vec<f32> = params.iter().cloned().collect();
            params_hat[i] = p + EPSILON;
            critical_positions.iter()
                .zip(results)
                .map(|(board, &game_result)| {
                    let score1 = board.static_eval_with_params(params);
                    let score2 = board.static_eval_with_params(&params_hat);
                    error(score1, game_result) - error(score2, game_result)
                })
                .sum()
        })
        .collect()
}

pub fn average_error<B>(positions: &[B], results: &[GameResult], params: &[f32]) -> f32
    where B: TunableBoard + ExtendedBoard + PgnBoard + Send + Debug + Sync {
    assert_eq!(positions.len(), results.len());
    positions.into_par_iter().zip(results)
        .map(|(board, game_result)| {
            let color = board.side_to_move();
            if let (Score::Val(qsearc_eval), _) = qsearch(&mut board.clone(), Score::Loss(0),
                                                     Score::Win(0), params) {
                let eval = qsearc_eval * color.multiplier() as f32;
                error(eval, game_result.clone())

            }
            else {
                warn!("Found mate score while tuning {}.", board.to_fen());
                0.0
            }
        })
        .sum::<f32>() / (positions.len() as f32)

}

pub fn error(eval: f32, game_result: GameResult) -> f32 {
    let answer = match game_result {
        GameResult::WhiteWin => 1.0,
        GameResult::Draw => 0.5,
        GameResult::BlackWin => 0.0,
    };

    let error = f32::powf(answer - sigmoid(eval), 2.0);
    error
}

pub fn sigmoid(eval: f32) -> f32 {
    let k = 0.97;
    1.0 / (1.0 + 10.0_f32.powf(-k * eval / 4.0))
}

/// Run quiescence search and returns a score from the side to move's perspective
fn qsearch<B: ExtendedBoard>(board: &mut B, mut alpha: Score, beta: Score, params: &[f32])
    -> (Score, Vec<B::Move>)
    where B: TunableBoard + ExtendedBoard {
    let color = board.side_to_move();

    if let Some(result) = board.game_result() {
        return (Score::from_game_result(result, color), vec![])
    }

    let stand_pat = Score::Val(board.static_eval_with_params(params) * color.multiplier() as f32);

    if stand_pat >= beta {
        return (beta, vec![]);
    }

    alpha = alpha.max(stand_pat);
    let mut best_pv = vec![];
    let mut best_move = None;

    let mut active_moves = vec![];
    board.active_moves(&mut active_moves);

    for mv in active_moves {
        let reverse_move = board.do_move(mv.clone());

        let (mut score, mut pv) = qsearch(board, !beta, !alpha, params);

        score = !score;
        board.reverse_move(reverse_move);

        if score >= beta {
            return (score, vec![mv]);
        }
        if score > alpha {
            alpha = score;
            best_pv = pv;
            best_move = Some(mv);
        }
    }
    best_pv.extend(best_move);
    return (alpha, best_pv);

}<commit_msg>Stop gradient descent after failing to lower error 5 consecutive times<commit_after>use search_algorithms::board::ExtendedBoard;
use search_algorithms::board::GameResult;
use search_algorithms::alpha_beta::Score;
use pgn::PgnBoard;
use rayon::prelude::*;
use std::fmt::Debug;

pub trait TunableBoard {
    const PARAMS: &'static [f32];

    fn static_eval_with_params(&self, params: &[f32]) -> f32;
}

pub fn get_critical_positions<B>(positions: &[B], params: &[f32]) -> Vec<B>
    where B: TunableBoard + ExtendedBoard + PgnBoard + Send + Sync {
    positions.into_par_iter()
        .map(|board| {
            if let (Score::Val(_), pv) = qsearch(&mut board.clone(), Score::Loss(0),
                                     Score::Win(0), params) {
                let mut cloned_board = board.clone();

                for mv in pv.into_iter().rev() {
                    cloned_board.do_move(mv);
                }
                cloned_board
            }
            else {
                warn!("Found mate score while tuning {}.", board.to_fen());
                board.clone()
            }

        })
        .collect()
}

pub fn gradient_descent<B>(positions: &[B], results: &[GameResult],
                           test_positions: &[B], test_results: &[GameResult],
                           params: &[f32]) -> Vec<f32>
    where B: TunableBoard + ExtendedBoard + PgnBoard + Send + Debug + Sync {
    assert_eq!(positions.len(), results.len());
    assert_eq!(test_positions.len(), test_results.len());

    let mut eta = 0.1;
    let beta = 0.8;

    let initial_error = average_error(test_positions, test_results, params);
    println!("Initial error: {}", initial_error);

    let mut errors = vec![initial_error];
    let mut lowest_error = initial_error;
    let mut paramss: Vec<Vec<f32>> = vec![params.to_vec()];
    let mut best_params = params.to_vec();
    let mut gradients = vec![0.0; params.len()];

    loop {
        let last_params = paramss.last().unwrap().clone();
        let slopes = calc_slope(positions, results, &last_params);
        gradients = gradients.iter()
            .zip(slopes)
            .map(|(gradient, slope)| beta * gradient + (1.0 - beta) * slope)
            .collect();
        println!("Gradients: {:?}", gradients);

        let new_params: Vec<f32> = last_params.iter()
            .zip(gradients.iter())
            .map(|(param, gradient)| param + gradient * eta)
            .collect();
        println!("New parameters: {:?}", new_params);

        let error = average_error(test_positions, test_results, &new_params);
        println!("Error now {}\n", error);

        if error < lowest_error {
            lowest_error = error;
            best_params = new_params.to_vec();
        }
        else if errors.len() >= 5
            && (1..=5).all(|i| errors[errors.len() - i] > lowest_error)
        {
            if eta < 0.005 {
                return best_params
            }
            else {
                eta = eta / 10.0;
                paramss = vec![best_params.clone()];
                errors = vec![lowest_error];
                println!("Reduced eta to {}\n", eta);
                continue;
            }
        }
        errors.push(error);
        paramss.push(new_params);
    }
}

pub fn calc_slope<B>(positions: &[B], results: &[GameResult], params: &[f32]) -> Vec<f32>
    where B: TunableBoard + ExtendedBoard + PgnBoard + Send + Sync {

    let critical_positions = get_critical_positions(positions, params);

    const EPSILON : f32 = 0.001;

    params.par_iter().enumerate()
        .map(|(i, p)| {
            let mut params_hat: Vec<f32> = params.iter().cloned().collect();
            params_hat[i] = p + EPSILON;
            critical_positions.iter()
                .zip(results)
                .map(|(board, &game_result)| {
                    let score1 = board.static_eval_with_params(params);
                    let score2 = board.static_eval_with_params(&params_hat);
                    error(score1, game_result) - error(score2, game_result)
                })
                .sum()
        })
        .collect()
}

pub fn average_error<B>(positions: &[B], results: &[GameResult], params: &[f32]) -> f32
    where B: TunableBoard + ExtendedBoard + PgnBoard + Send + Debug + Sync {
    assert_eq!(positions.len(), results.len());
    positions.into_par_iter().zip(results)
        .map(|(board, game_result)| {
            let color = board.side_to_move();
            if let (Score::Val(qsearc_eval), _) = qsearch(&mut board.clone(), Score::Loss(0),
                                                     Score::Win(0), params) {
                let eval = qsearc_eval * color.multiplier() as f32;
                error(eval, game_result.clone())

            }
            else {
                warn!("Found mate score while tuning {}.", board.to_fen());
                0.0
            }
        })
        .sum::<f32>() / (positions.len() as f32)

}

pub fn error(eval: f32, game_result: GameResult) -> f32 {
    let answer = match game_result {
        GameResult::WhiteWin => 1.0,
        GameResult::Draw => 0.5,
        GameResult::BlackWin => 0.0,
    };

    let error = f32::powf(answer - sigmoid(eval), 2.0);
    error
}

pub fn sigmoid(eval: f32) -> f32 {
    let k = 0.97;
    1.0 / (1.0 + 10.0_f32.powf(-k * eval / 4.0))
}

/// Run quiescence search and returns a score from the side to move's perspective
fn qsearch<B: ExtendedBoard>(board: &mut B, mut alpha: Score, beta: Score, params: &[f32])
    -> (Score, Vec<B::Move>)
    where B: TunableBoard + ExtendedBoard {
    let color = board.side_to_move();

    if let Some(result) = board.game_result() {
        return (Score::from_game_result(result, color), vec![])
    }

    let stand_pat = Score::Val(board.static_eval_with_params(params) * color.multiplier() as f32);

    if stand_pat >= beta {
        return (beta, vec![]);
    }

    alpha = alpha.max(stand_pat);
    let mut best_pv = vec![];
    let mut best_move = None;

    let mut active_moves = vec![];
    board.active_moves(&mut active_moves);

    for mv in active_moves {
        let reverse_move = board.do_move(mv.clone());

        let (mut score, mut pv) = qsearch(board, !beta, !alpha, params);

        score = !score;
        board.reverse_move(reverse_move);

        if score >= beta {
            return (score, vec![mv]);
        }
        if score > alpha {
            alpha = score;
            best_pv = pv;
            best_move = Some(mv);
        }
    }
    best_pv.extend(best_move);
    return (alpha, best_pv);

}<|endoftext|>