<commit_before>use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::Hash;
use std::rc::Rc;
use std::cell::RefCell;

struct Rlru<K, V>
    where K: Eq + Hash
{
    cache: HashMap<K, Rc<RefCell<Node<V>>>>,
    head: Link<V>,
    tail: Link<V>,
    max_length: u32,
}

struct Node<T> {
    elem: Option<Rc<RefCell<T>>>,
    prev: Link<T>,
    next: Link<T>,
}

type Link<T> = Option<Rc<RefCell<Node<T>>>>;

impl<K, V> Rlru<K, V>
    where K: Eq + Hash
{
    pub fn new() -> Self {
        Rlru {
            cache: HashMap::new(),
            head: None,
            tail: None,
            max_length: 0,
        }
    }

    fn length_upkeep(&mut self) -> &mut Self {
        let max = self.max_length as usize;
        match self.cache.len() {
            0...max => &mut self,
            // placeholder. Will pop off LRU key.
            _ => &mut self
        }
    }

    fn splice_node(&mut self, new_node: Node<V>) {
        // update node's prev/next pointers to link to each other
        // insert node at head of rlru.
    }

    pub fn get(&mut self, key: K) -> Option<&V> {
        match self.cache.entry(key) {
            Entry::Occupied(node) => {
                // splice node to front
            },
            Entry::Vacant(_) => None
        }
    }

    pub fn set(&mut self, key: K, elem: V) -> &mut Self {
        match self.cache.entry(key) {
            Entry::Occupied(node) => {
                // splice node to front & update value
                &mut self;
            },
            Entry::Vacant(_) =>  {
                // insert node to front & do ln upkeep
                &mut self;
            }
        }
    }
}
<commit_msg>internal upkeep methods for lru<commit_after>use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::Hash;
use std::rc::Rc;
// use std::cell::RefCell;

struct Rlru<K, V>
    where K: Eq + Hash
{
    cache: HashMap<K, Rc<Node<V>>>,
    head: Link<V>,
    tail: Link<V>,
    max_length: u32,
}

struct Node<T> {
    elem: T,
    prev: Link<T>,
    next: Link<T>,
}

impl<T> Node<T> {
    fn new(elem: T) -> Self {
        Node {
            elem: elem,
            prev: None,
            next: None,
        }
    }
}

type Link<T> = Option<Rc<Node<T>>>;

impl<K, V> Rlru<K, V>
    where K: Eq + Hash
{
    pub fn new() -> Self {
        Rlru {
            cache: HashMap::new(),
            head: None,
            tail: None,
            max_length: 0,
        }
    }

    fn length_upkeep(&mut self) -> &mut Self {
        let max = self.max_length as usize;
        match self.cache.len() {
            0...max => self,
            // placeholder. Will pop off LRU key.
            _ => self
        }
    }

    fn splice_node(&mut self, node: Node<V>) {
        // update node's prev/next pointers to link to each other
        // insert node at head of rlru.
    }

    fn push(&mut self, node: Node<V>) -> &mut Self {
        let node = Rc::new(node);
        self.head.take().map(|prev_head| {
            prev_head.prev = Some(node.clone());
            node.next = Some(prev_head);
        });
        self.head = Some(node.clone());
        self
    }

    fn pop(&mut self) {
        self.tail.take().map(|node| {
            if node.prev.is_none() {
                // have to worry about updating head.
                self.head.take();
            }
            self.tail = node.prev;
        });
    }

    pub fn get(&mut self, key: K) -> Option<&V> {
        match self.cache.entry(key) {
            Entry::Occupied(node) => {
                // splice node to front
            },
            Entry::Vacant(_) => None
        }
    }

    pub fn set(&mut self, key: K, elem: V) -> &mut Self {
        match self.cache.entry(key) {
            Entry::Occupied(node) => {
                // splice node to front & update value
                self
            },
            Entry::Vacant(_) =>  {
                // insert node to front & do ln upkeep
                self
            }
        }
    }
}
<|endoftext|>


<commit_before>// Semantics analyzer. This pass plays a few important roles as shown
// below:
//
// - Add types to nodes. For example, a tree that represents "1+2" is
//   typed as INT because the result type of an addition of two
//   integers is integer.
//
// - Insert nodes to make array-to-pointer conversion explicit.
//   Recall that, in C, "array of T" is automatically converted to
//   "pointer to T" in most contexts.
//
// - Scales operands for pointer arithmetic. E.g. ptr+1 becomes ptr+4
//   for integer and becomes ptr+8 for pointer.
//
// - Reject bad assignments, such as `1=2+3`.

use crate::parse::*;
use crate::token::*;
use crate::util::*;
use std::cell::RefCell;
use std::mem;
use std::rc::Rc;

fn swap(p: &mut Node, q: &mut Node) {
    mem::swap(p, q);
}

fn maybe_decay(base: & mut Node, decay: bool) -> & mut Node {
    if !decay || base.ty.borrow().ty != CType::ARY {
        return base;
    }

    let tmp = base.clone();
    *base = alloc_node();
    base.op = NodeType::ADDR;

    let node_ty = tmp.clone().ty;
    let ty_tmp = *node_ty.borrow().clone().ary_of.unwrap();
    base.ty = Rc::new(RefCell::new(ptr_to(Rc::new(RefCell::new(ty_tmp)))));

    base.expr = Some(Box::new(tmp));
    return base;
}

macro_rules! bad_node {
    ($node:expr, $msg:expr) => {
        bad_token(&*$node.token.clone().unwrap(), $msg.to_string());
        panic!();
    };
}

fn check_lval(node: Box<Node>) {
    match node.op {
        NodeType::VAR | NodeType::DEREF | NodeType::DOT => { }
        _ => {
            bad_node!(node, "not an lvalue");
        }
    }
}

fn scale_ptr(op: NodeType, node: Node, ty: Type) -> Node {
    let mut e = alloc_node();
    e.op = op;
    e.lhs = Some(Box::new(node.clone()));
    let ptr = ty.ptr_to.unwrap();
    e.rhs = Some(Box::new(new_int_node(ptr.borrow().size, node.token)));
    return e;
}

fn walk<'a>(node: &'a mut Node, prog: &'a mut Program) -> &'a Node {
    return do_walk(node, true, prog);
}

fn walk_noconv<'a>(node: &'a mut Node, prog: &'a mut Program) -> &'a Node {
    return do_walk(node, false, prog);
}

fn do_walk<'a>(node: &'a mut Node, decay: bool, prog: &'a mut Program) -> &'a Node {
    match node.op {
        NodeType::NUM |
            NodeType::NULL |
            NodeType::BREAK => {
            return node;
        }
        NodeType::VAR => {
            return maybe_decay(node, decay);
        }
        NodeType::VARDEF => {
            match node.init {
                Some(_) => {
                    node.init = Some(Box::new(walk(&mut *node.init.clone().unwrap(), prog).clone()));
                }
                None => { }
            }
            return node;
        }
        NodeType::IF => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.then = Some(Box::new(walk(&mut *node.then.clone().unwrap(), prog).clone()));

            if node.els.is_some() {
                node.els = Some(Box::new(walk(&mut *node.els.clone().unwrap(), prog).clone()));
            }
            return node;
        }
        NodeType::FOR => {
            node.init = Some(Box::new(walk(&mut *node.init.clone().unwrap(), prog).clone()));
            if let Some(mut cond) = node.cond.clone() {
                node.cond = Some(Box::new(walk(&mut cond, prog).clone()));
            }
            if let Some(mut inc) = node.inc.clone() {
                node.inc = Some(Box::new(walk(&mut inc, prog).clone()));
            }
            node.body = Some(Box::new(walk(&mut *node.body.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::DO_WHILE => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.body = Some(Box::new(walk(&mut *node.body.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::ADD => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

            match (&mut node.lhs, &mut node.rhs) {
                (Some(ref mut lhs), Some(ref mut rhs)) => {
                    if rhs.ty.borrow().ty == CType::PTR {
                        swap(lhs, rhs);
                    }
                }
                _ => {}
            }

            if let Some(ref rhs) = node.rhs {
                if rhs.ty.borrow().ty == CType::PTR {
                    bad_node!(node, "pointer + pointer");
                }
            }

            if let Some(ref lhs) = node.lhs {
                if lhs.ty.borrow().ty == CType::PTR {
                    node.rhs = Some(Box::new(scale_ptr(NodeType::MUL, *node.rhs.clone().unwrap(), lhs.ty.borrow().clone())));
                }
            }

            if let Some(ref lhs) = node.lhs {
                node.ty = lhs.ty.clone();
            }
            return node;
        }
        NodeType::SUB => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

            let l = &node.rhs.clone().unwrap().ty;
            let r = &node.lhs.clone().unwrap().ty;
            if l.borrow().ty == CType::PTR && r.borrow().ty == CType::PTR {
                if !same_type(r.clone(), l.clone()) {
                    bad_node!(node, "incompatible pointer");
                }
                *node = scale_ptr(NodeType::DIV, node.clone(), l.borrow().clone());
            }

            node.ty = node.lhs.clone().unwrap().ty;
            return node;
        }
        NodeType::ADD_EQ |
            NodeType::SUB_EQ => {
                node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
                check_lval(node.lhs.clone().unwrap());
                node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
                if let Some(ref lhs) = node.lhs {
                    node.ty = lhs.ty.clone();
                }

                if let Some(ref lhs) = node.lhs {
                    if lhs.ty.borrow().ty == CType::PTR {
                        node.rhs = Some(Box::new(scale_ptr(NodeType::MUL, *node.rhs.clone().unwrap(), lhs.ty.borrow().clone())));
                    }
                }
                return node;
        }

        NodeType::EQL |
            NodeType::MUL_EQ |
            NodeType::DIV_EQ |
            NodeType::MOD_EQ |
            NodeType::SHL_EQ |
            NodeType::SHR_EQ |
            NodeType::BITAND_EQ |
            NodeType::XOR_EQ |
            NodeType::BITOR_EQ => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            check_lval(node.lhs.clone().unwrap());

            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
            node.ty = node.lhs.clone().unwrap().ty;
            return node;
        }
        NodeType::DOT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            let node_ty = node.expr.clone().unwrap().ty;
            if node_ty.borrow().ty != CType::STRUCT {
                bad_node!(node, "struct expected before '.'");
            }

            let ty = node.expr.clone().unwrap().ty;
            if ty.borrow().members == None {
                bad_node!(node, format!("incomplete type: {:?}", node.expr));
            }

            for m in ty.borrow().clone().members.unwrap().iter() {
                if m.name != node.name {
                    continue;
                }

                node.ty = m.ty.clone();
                return maybe_decay(node, decay);
            }
            bad_node!(node, format!("member missing: {}", node.name));
        }
        NodeType::QUEST => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.then = Some(Box::new(walk(&mut *node.then.clone().unwrap(), prog).clone()));
            node.els = Some(Box::new(walk(&mut *node.els.clone().unwrap(), prog).clone()));
            node.ty = node.then.clone().unwrap().ty;
            return node;
        }
        NodeType::MUL |
            NodeType::DIV |
            NodeType::MOD |
            NodeType::LT |
            NodeType::OR |
            NodeType::XOR |
            NodeType::AND |
            NodeType::EQ |
            NodeType::NE |
            NodeType::LE |
            NodeType::SHL |
            NodeType::SHR |
            NodeType::LOGAND |
            NodeType::LOGOR => {
                node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
                node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

                match node.lhs {
                    Some(ref lhs) => {
                        node.ty = lhs.ty.clone();
                    }
                    None => { }
                }
                return node;
            }
        NodeType::COMMA => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
            node.ty = node.rhs.clone().unwrap().ty;
            return node;
        }
        NodeType::POST_INC |
            NodeType::POST_DEC |
            NodeType::NEG |
            NodeType::EXCLAM |
            NodeType::NOT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            node.ty = node.expr.clone().unwrap().ty;
            return node;
        }
        NodeType::ADDR => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            check_lval(node.expr.clone().unwrap());
            node.ty = Rc::new(RefCell::new(ptr_to(node.expr.clone().unwrap().ty)));
            return node;
        }
        NodeType::DEREF => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));

            match &mut node.expr {
                Some(ref expr) => {
                    if expr.ty.borrow().ty != CType::PTR {
                        bad_node!(node, format!("operand must be a pointer: {:?}", expr));
                    }

                    let ptr = expr.ty.borrow().ptr_to.clone().unwrap();
                    if ptr.borrow().ty == CType::VOID {
                        bad_node!(node, "operand dereference void pointer");
                    }
                }
                None => {}
            }

            let ty_tmp = node.expr.clone().unwrap().ty;
            node.ty = ty_tmp.borrow().clone().ptr_to.unwrap();
            return maybe_decay(node, decay);
        }
        NodeType::RETURN |
            NodeType::EXPR_STMT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::CALL => {
            for i in 0..node.args.len() {
                node.args[i] = walk(&mut node.args[i], prog).clone();
            }
            return node;
        }
        NodeType::COMP_STMT => {
            for i in 0..node.stmts.len() {
                node.stmts[i] = walk(&mut node.stmts[i], prog).clone();
            }
            return node;
        }
        NodeType::STMT_EXPR => {
            node.body = Some(Box::new(walk(&mut *node.body.clone().unwrap(), prog).clone()));
            node.ty = Rc::new(RefCell::new(int_ty()));
            return node;
        }
        _ => {
            panic!("unknown node type: {:?}", node);
        }
    }
}

fn sema_funcdef(node: Rc<RefCell<Node>>, prog: &mut Program) {
    let mut args = Vec::new();
    for a in node.borrow_mut().args.iter_mut() {
        args.push(walk(&mut a.clone(), prog).clone());
    }
    node.borrow_mut().args = args;
    let mut body = node.borrow_mut().body.clone().unwrap();
    node.borrow_mut().body = Some(Box::new(walk(&mut body, prog).clone()));

    let mut off = 0;
    for v in node.borrow_mut().lvars.iter_mut() {
        off = roundup(off, v.borrow().ty.align);
        off += v.borrow().ty.size;
        v.borrow_mut().offset = off;
    }
    node.borrow_mut().stacksize = off;
}

pub fn get_type(node: &mut Node) -> Type {
    let mut prog = new_program();
    return walk_noconv(node, &mut prog).ty.borrow().clone();
}

pub fn sema(prog: &mut Program) {
    let nodes = prog.nodes.clone();

    for node in nodes.iter() {
        if node.borrow().op == NodeType::DECL {
            continue;
        }

        assert!(node.borrow().op == NodeType::FUNC);
        sema_funcdef(node.clone(), prog);
    }

    prog.nodes = nodes;
}
<commit_msg>Simplify.<commit_after>// Semantics analyzer. This pass plays a few important roles as shown
// below:
//
// - Add types to nodes. For example, a tree that represents "1+2" is
//   typed as INT because the result type of an addition of two
//   integers is integer.
//
// - Insert nodes to make array-to-pointer conversion explicit.
//   Recall that, in C, "array of T" is automatically converted to
//   "pointer to T" in most contexts.
//
// - Scales operands for pointer arithmetic. E.g. ptr+1 becomes ptr+4
//   for integer and becomes ptr+8 for pointer.
//
// - Reject bad assignments, such as `1=2+3`.

use crate::parse::*;
use crate::token::*;
use crate::util::*;
use std::cell::RefCell;
use std::mem;
use std::rc::Rc;

fn swap(p: &mut Node, q: &mut Node) {
    mem::swap(p, q);
}

fn maybe_decay(base: & mut Node, decay: bool) -> & mut Node {
    if !decay || base.ty.borrow().ty != CType::ARY {
        return base;
    }

    let tmp = base.clone();
    *base = alloc_node();
    base.op = NodeType::ADDR;

    let node_ty = tmp.clone().ty;
    let ty_tmp = *node_ty.borrow().clone().ary_of.unwrap();
    base.ty = Rc::new(RefCell::new(ptr_to(Rc::new(RefCell::new(ty_tmp)))));

    base.expr = Some(Box::new(tmp));
    return base;
}

macro_rules! bad_node {
    ($node:expr, $msg:expr) => {
        bad_token(&*$node.token.clone().unwrap(), $msg.to_string());
        panic!();
    };
}

fn check_lval(node: Box<Node>) {
    match node.op {
        NodeType::VAR | NodeType::DEREF | NodeType::DOT => { }
        _ => {
            bad_node!(node, "not an lvalue");
        }
    }
}

fn scale_ptr(op: NodeType, node: Node, ty: Type) -> Node {
    let mut e = alloc_node();
    e.op = op;
    e.lhs = Some(Box::new(node.clone()));
    let ptr = ty.ptr_to.unwrap();
    e.rhs = Some(Box::new(new_int_node(ptr.borrow().size, node.token)));
    return e;
}

fn check_int(node: & Node) {
    if node.ty.borrow().ty != CType::INT {
        if node.ty.borrow().ty != CType::CHAR {
            bad_node!(node, "not an integer");
        }
    }
}

fn walk<'a>(node: &'a mut Node, prog: &'a mut Program) -> &'a Node {
    return do_walk(node, true, prog);
}

fn walk_noconv<'a>(node: &'a mut Node, prog: &'a mut Program) -> &'a Node {
    return do_walk(node, false, prog);
}

fn do_walk<'a>(node: &'a mut Node, decay: bool, prog: &'a mut Program) -> &'a Node {
    match node.op {
        NodeType::NUM |
            NodeType::NULL |
            NodeType::BREAK => {
            return node;
        }
        NodeType::VAR => {
            return maybe_decay(node, decay);
        }
        NodeType::VARDEF => {
            match node.init {
                Some(_) => {
                    node.init = Some(Box::new(walk(&mut *node.init.clone().unwrap(), prog).clone()));
                }
                None => { }
            }
            return node;
        }
        NodeType::IF => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.then = Some(Box::new(walk(&mut *node.then.clone().unwrap(), prog).clone()));

            if node.els.is_some() {
                node.els = Some(Box::new(walk(&mut *node.els.clone().unwrap(), prog).clone()));
            }
            return node;
        }
        NodeType::FOR => {
            node.init = Some(Box::new(walk(&mut *node.init.clone().unwrap(), prog).clone()));
            if let Some(mut cond) = node.cond.clone() {
                node.cond = Some(Box::new(walk(&mut cond, prog).clone()));
            }
            if let Some(mut inc) = node.inc.clone() {
                node.inc = Some(Box::new(walk(&mut inc, prog).clone()));
            }
            node.body = Some(Box::new(walk(&mut *node.body.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::DO_WHILE => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.body = Some(Box::new(walk(&mut *node.body.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::ADD => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

            match (&mut node.lhs, &mut node.rhs) {
                (Some(ref mut lhs), Some(ref mut rhs)) => {
                    if rhs.ty.borrow().ty == CType::PTR {
                        swap(lhs, rhs);
                    }
                }
                _ => {}
            }

            if let Some(ref rhs) = node.rhs {
                check_int(rhs);
            }

            if let Some(ref lhs) = node.lhs {
                if lhs.ty.borrow().ty == CType::PTR {
                    node.rhs = Some(Box::new(scale_ptr(NodeType::MUL, *node.rhs.clone().unwrap(), lhs.ty.borrow().clone())));
                }
            }

            if let Some(ref lhs) = node.lhs {
                node.ty = lhs.ty.clone();
            }
            return node;
        }
        NodeType::SUB => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

            let l = &node.rhs.clone().unwrap().ty;
            let r = &node.lhs.clone().unwrap().ty;
            if l.borrow().ty == CType::PTR && r.borrow().ty == CType::PTR {
                if !same_type(r.clone(), l.clone()) {
                    bad_node!(node, "incompatible pointer");
                }
                *node = scale_ptr(NodeType::DIV, node.clone(), l.borrow().clone());
            }

            node.ty = node.lhs.clone().unwrap().ty;
            return node;
        }
        NodeType::ADD_EQ |
            NodeType::SUB_EQ => {
                node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
                check_lval(node.lhs.clone().unwrap());
                node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
                if let Some(ref lhs) = node.lhs {
                    node.ty = lhs.ty.clone();
                }

                if let Some(ref lhs) = node.lhs {
                    if lhs.ty.borrow().ty == CType::PTR {
                        node.rhs = Some(Box::new(scale_ptr(NodeType::MUL, *node.rhs.clone().unwrap(), lhs.ty.borrow().clone())));
                    }
                }
                return node;
        }

        NodeType::EQL |
            NodeType::MUL_EQ |
            NodeType::DIV_EQ |
            NodeType::MOD_EQ |
            NodeType::SHL_EQ |
            NodeType::SHR_EQ |
            NodeType::BITAND_EQ |
            NodeType::XOR_EQ |
            NodeType::BITOR_EQ => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            check_lval(node.lhs.clone().unwrap());

            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
            node.ty = node.lhs.clone().unwrap().ty;
            return node;
        }
        NodeType::DOT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            let node_ty = node.expr.clone().unwrap().ty;
            if node_ty.borrow().ty != CType::STRUCT {
                bad_node!(node, "struct expected before '.'");
            }

            let ty = node.expr.clone().unwrap().ty;
            if ty.borrow().members == None {
                bad_node!(node, format!("incomplete type: {:?}", node.expr));
            }

            for m in ty.borrow().clone().members.unwrap().iter() {
                if m.name != node.name {
                    continue;
                }

                node.ty = m.ty.clone();
                return maybe_decay(node, decay);
            }
            bad_node!(node, format!("member missing: {}", node.name));
        }
        NodeType::QUEST => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.then = Some(Box::new(walk(&mut *node.then.clone().unwrap(), prog).clone()));
            node.els = Some(Box::new(walk(&mut *node.els.clone().unwrap(), prog).clone()));
            node.ty = node.then.clone().unwrap().ty;
            return node;
        }
        NodeType::MUL |
            NodeType::DIV |
            NodeType::MOD |
            NodeType::LT |
            NodeType::OR |
            NodeType::XOR |
            NodeType::AND |
            NodeType::EQ |
            NodeType::NE |
            NodeType::LE |
            NodeType::SHL |
            NodeType::SHR |
            NodeType::LOGAND |
            NodeType::LOGOR => {
                node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
                node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

                check_int(&*node.lhs.clone().unwrap());
                check_int(&*node.rhs.clone().unwrap());

                match node.lhs {
                    Some(ref lhs) => {
                        node.ty = lhs.ty.clone();
                    }
                    None => { }
                }
                return node;
            }
        NodeType::COMMA => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
            node.ty = node.rhs.clone().unwrap().ty;
            return node;
        }
        NodeType::POST_INC |
            NodeType::POST_DEC |
            NodeType::NEG |
            NodeType::EXCLAM |
            NodeType::NOT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            node.ty = node.expr.clone().unwrap().ty;
            return node;
        }
        NodeType::ADDR => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            check_lval(node.expr.clone().unwrap());
            node.ty = Rc::new(RefCell::new(ptr_to(node.expr.clone().unwrap().ty)));
            return node;
        }
        NodeType::DEREF => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));

            match &mut node.expr {
                Some(ref expr) => {
                    if expr.ty.borrow().ty != CType::PTR {
                        bad_node!(node, format!("operand must be a pointer: {:?}", expr));
                    }

                    let ptr = expr.ty.borrow().ptr_to.clone().unwrap();
                    if ptr.borrow().ty == CType::VOID {
                        bad_node!(node, "operand dereference void pointer");
                    }
                }
                None => {}
            }

            let ty_tmp = node.expr.clone().unwrap().ty;
            node.ty = ty_tmp.borrow().clone().ptr_to.unwrap();
            return maybe_decay(node, decay);
        }
        NodeType::RETURN |
            NodeType::EXPR_STMT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::CALL => {
            for i in 0..node.args.len() {
                node.args[i] = walk(&mut node.args[i], prog).clone();
            }
            let node_ty = *node.ty.borrow().clone().returning.unwrap();
            node.ty = Rc::new(RefCell::new(node_ty));
            return node;
        }
        NodeType::COMP_STMT => {
            for i in 0..node.stmts.len() {
                node.stmts[i] = walk(&mut node.stmts[i], prog).clone();
            }
            return node;
        }
        NodeType::STMT_EXPR => {
            node.body = Some(Box::new(walk(&mut *node.body.clone().unwrap(), prog).clone()));
            node.ty = Rc::new(RefCell::new(int_ty()));
            return node;
        }
        _ => {
            panic!("unknown node type: {:?}", node);
        }
    }
}

fn sema_funcdef(node: Rc<RefCell<Node>>, prog: &mut Program) {
    let mut args = Vec::new();
    for a in node.borrow_mut().args.iter_mut() {
        args.push(walk(&mut a.clone(), prog).clone());
    }
    node.borrow_mut().args = args;
    let mut body = node.borrow_mut().body.clone().unwrap();
    node.borrow_mut().body = Some(Box::new(walk(&mut body, prog).clone()));

    let mut off = 0;
    for v in node.borrow_mut().lvars.iter_mut() {
        off = roundup(off, v.borrow().ty.align);
        off += v.borrow().ty.size;
        v.borrow_mut().offset = off;
    }
    node.borrow_mut().stacksize = off;
}

pub fn get_type(node: &mut Node) -> Type {
    let mut prog = new_program();
    return walk_noconv(node, &mut prog).ty.borrow().clone();
}

pub fn sema(prog: &mut Program) {
    let nodes = prog.nodes.clone();

    for node in nodes.iter() {
        if node.borrow().op == NodeType::DECL {
            continue;
        }

        assert!(node.borrow().op == NodeType::FUNC);
        sema_funcdef(node.clone(), prog);
    }

    prog.nodes = nodes;
}
<|endoftext|>


<commit_before>use std::io;
use std::io::fs::PathExtensions;
use std::io::process::Command;
use std::fmt::{Show, Formatter, Error};
use std::collections::HashSet;

use app_result::{AppResult, app_err};
use dependencies::SourceKind;

use dirs::{
   rusty_tags_cache_dir,
   cargo_git_src_dir,
   cargo_crates_io_src_dir,
   glob_path
};

pub struct Tags
{
   /// the root directory of the source code
   /// for which the tags have been created
   pub src_dir: Path,

   /// the tags file of the sources in `src_dir`
   pub tags_file: Path,

   /// indicates if the tags file is already existing
   /// and the cached tags file is returned
   pub cached: bool
}

/// Checks if there's already a tags file for `src_kind`
/// and if not it's creating a new tags file and returning it.
pub fn update_tags(src_kind: &SourceKind) -> AppResult<Tags>
{
   let cache_dir = try!(rusty_tags_cache_dir());

   let mut src_tags = cache_dir.clone();
   src_tags.push(src_kind.tags_file_name());

   let src_dir = try!(find_src_dir(src_kind));
   if src_tags.is_file() {
      return Ok(Tags { src_dir: src_dir, tags_file: src_tags, cached: true });
   }

   try!(create_tags(&src_dir, &src_tags));
   Ok(Tags { src_dir: src_dir, tags_file: src_tags, cached: false })
}

/// Does the same thing as `update_tags`, but also checks if the `lib.rs`
/// file of the library has public reexports of external crates. If
/// that's the case, then the tags of the public reexported external
/// crates are merged into the tags of the library.
pub fn update_tags_and_check_for_reexports(src_kind: &SourceKind, dependencies: &Vec<SourceKind>) -> AppResult<Tags>
{
   let lib_tags = try!(update_tags(src_kind));

   let reexp_crates = try!(find_reexported_crates(&lib_tags.src_dir));
   if reexp_crates.is_empty() {
      return Ok(lib_tags);
   }

   println!("Found public reexports in '{}' of:", src_kind.get_lib_name());
   for rcrate in reexp_crates.iter() {
      println!("   {}", rcrate);
   }
   println!("");

   let mut crate_tags = Vec::<Path>::new();
   for rcrate in reexp_crates.iter() {
      if let Some(crate_dep) = dependencies.iter().find(|d| d.get_lib_name() == *rcrate) {
         crate_tags.push(try!(update_tags(crate_dep)).tags_file.clone());
      }
   }

   if crate_tags.is_empty() {
      return Ok(lib_tags);
   }

   crate_tags.push(lib_tags.tags_file.clone());
   try!(merge_tags(&crate_tags, &lib_tags.tags_file));
   Ok(lib_tags)
}

/// merges `tag_files` into `into_tag_file`
pub fn merge_tags(tag_files: &Vec<Path>, into_tag_file: &Path) -> AppResult<()>
{
   println!("Merging ...\n   tags:");

   let mut file_contents: Vec<String> = Vec::new();
   for file in tag_files.iter() {
      println!("      {}", file.display());
      file_contents.push(try!(io::File::open(file).read_to_string()));
   }

   println!("\n   into:\n      {}\n", into_tag_file.display());

   let mut merged_lines: Vec<&str> = Vec::with_capacity(100_000);
   for content in file_contents.iter() {
      for line in content.as_slice().lines_any() {
         if ! line.is_empty() && line.char_at(0) != '!' {
            merged_lines.push(line);
         }
      }
   }

   merged_lines.sort();

   let mut tag_file = try!(io::File::open_mode(into_tag_file, io::Truncate, io::ReadWrite));
   try!(tag_file.write_line("!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;\" to lines/"));
   try!(tag_file.write_line("!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/"));

   for line in merged_lines.iter() {
      try!(tag_file.write_line(*line));
   }

   Ok(())
}

/// creates tags recursive for the directory hierarchy starting at `src_dir`
/// and writes them to `tags_file`
pub fn create_tags(src_dir: &Path, tags_file: &Path) -> AppResult<()>
{
   let mut cmd = Command::new("ctags");
   cmd.arg("--recurse")
      .arg("--langdef=Rust")
      .arg("--langmap=Rust:.rs")
      .arg("--regex-Rust=/^[ \\t]*(#\\[[^\\]]\\][ \\t]*)*(pub[ \\t]+)?(extern[ \\t]+)?(\"[^\"]+\"[ \\t]+)?(unsafe[ \\t]+)?fn[ \\t]+([a-zA-Z0-9_]+)/\\6/f,functions,function definitions/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?type[ \\t]+([a-zA-Z0-9_]+)/\\2/T,types,type definitions/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?enum[ \\t]+([a-zA-Z0-9_]+)/\\2/g,enum,enumeration names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?struct[ \\t]+([a-zA-Z0-9_]+)/\\2/s,structure names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?mod[ \\t]+([a-zA-Z0-9_]+)/\\2/m,modules,module names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?static[ \\t]+([a-zA-Z0-9_]+)/\\2/c,consts,static constants/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?trait[ \\t]+([a-zA-Z0-9_]+)/\\2/t,traits,traits/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?impl([ \\t\\n]*<[^>]*>)?[ \\t]+(([a-zA-Z0-9_:]+)[ \\t]*(<[^>]*>)?[ \\t]+(for)[ \\t]+)?([a-zA-Z0-9_]+)/\\4 \\6 \\7/i,impls,trait implementations/")
      .arg("--regex-Rust=/^[ \\t]*macro_rules![ \\t]+([a-zA-Z0-9_]+)/\\1/d,macros,macro definitions/")
      .arg("-o")
      .arg(tags_file)
      .arg(src_dir);

   println!("Creating tags ...\n   for source:\n      {}\n\n   cached at:\n      {}\n",
            src_dir.display(), tags_file.display());

   try!(cmd.output());
   Ok(())
}

/// find the source directory of `src_kind`, for git sources the directories
/// in `~/.cargo/git/checkouts` are considered and for crates.io sources
/// the directories in `~/.cargo/registry/src/github.com-*` are considered
fn find_src_dir(src_kind: &SourceKind) -> AppResult<Path>
{
   match *src_kind {
      SourceKind::Git { ref lib_name, ref commit_hash } => {
         let mut lib_src = lib_name.clone();
         lib_src.push_str("-*");

         let mut src_dir = try!(cargo_git_src_dir());
         src_dir.push(&lib_src);
         src_dir.push("master");

         let mut src_paths = glob_path(&src_dir);
         for src_path in src_paths {
            let src_commit_hash = try!(get_commit_hash(&src_path));
            if *commit_hash == src_commit_hash {
               return Ok(src_path);
            }
         }

         // the git repository name hasn't to match the name of the library,
         // so here we're just going through all git directories and searching
         // for the one with a matching commit hash
         let mut src_dir = try!(cargo_git_src_dir());
         src_dir.push("*");
         src_dir.push("master");

         let mut src_paths = glob_path(&src_dir);
         for src_path in src_paths {
            let src_commit_hash = try!(get_commit_hash(&src_path));
            if *commit_hash == src_commit_hash {
               return Ok(src_path);
            }
         }

         Err(app_err(format!("
   Couldn't find git repository of the dependency '{}'!
   Have you run 'cargo build' at least once or have you added/updated a dependency without calling 'cargo build' again?", lib_name)))
      },

      SourceKind::CratesIo { ref lib_name, ref version } => {
         let mut lib_src = lib_name.clone();
         lib_src.push('-');
         lib_src.push_str(&**version);

         let mut src_dir = try!(cargo_crates_io_src_dir());
         src_dir.push(&lib_src);

         if ! src_dir.is_dir() {
            return Err(app_err(format!("
   Couldn't find source code of the dependency '{}'!
   Have you run 'cargo build' at least once or have you added/updated a dependency without calling 'cargo build' again?", lib_name)))
         }

         Ok(src_dir)
      }
   }
}

type CrateName = String;

/// searches in the file `<src_dir>/src/lib.rs` for external crates
/// that are reexpored and returns their names
fn find_reexported_crates(src_dir: &Path) -> AppResult<Vec<CrateName>>
{
   let mut lib_file = src_dir.clone();
   lib_file.push("src");
   lib_file.push("lib.rs");

   if ! lib_file.is_file() {
      return Ok(Vec::new());
   }

   let contents = try!(io::File::open(&lib_file).read_to_string());
   let mut lines = contents.lines_any();

   type ModuleName = String;
   let mut pub_uses = HashSet::<ModuleName>::new();

   #[deriving(Eq, PartialEq, Hash)]
   struct ExternCrate<'a>
   {
      name: &'a str,
      as_name: &'a str
   }

   let mut extern_crates = HashSet::<ExternCrate>::new();

   for line in lines {
      let items = line.trim_chars(';').split(' ').collect::<Vec<&str>>();
      if items.len() < 3 {
         continue;
      }

      if items[0] == "pub" && items[1] == "use" {
         let mods = items[2].split_str("::").collect::<Vec<&str>>();
         if mods.len() >= 1 {
            pub_uses.insert(mods[0].to_string());
         }
      }

      if items[0] == "extern" && items[1] == "crate" {
         if items.len() == 3 {
            extern_crates.insert(ExternCrate { name: items[2].trim_chars('"'), as_name: items[2] });
         }
         else if items.len() == 5 && items[3] == "as" {
            extern_crates.insert(ExternCrate { name: items[2].trim_chars('"'), as_name: items[4] });
         }
      }
   }

   let mut reexp_crates = Vec::<CrateName>::new();
   for extern_crate in extern_crates.iter() {
      if pub_uses.contains(extern_crate.as_name) {
         reexp_crates.push(extern_crate.name.to_string());
      }
   }

   Ok(reexp_crates)
}

/// get the commit hash of the current `HEAD` of the git repository located at `git_dir`
fn get_commit_hash(git_dir: &Path) -> AppResult<String>
{
   let mut cmd = Command::new("git");
   cmd.cwd(git_dir)
      .arg("rev-parse")
      .arg("HEAD");

   let out = try!(cmd.output());
   String::from_utf8(out.output)
      .map(|s| s.as_slice().trim().to_string())
      .map_err(|_| app_err("Couldn't convert git output to utf8!".to_string()))
}

impl Show for Tags
{
   fn fmt(&self, f: &mut Formatter) -> Result<(), Error>
   {
      write!(f, "Tags ( src_dir: {}, tags_file: {}, cached: {} )",
             self.src_dir.display(), self.tags_file.display(), self.cached)
   }
}
<commit_msg>Stop if tags are chached<commit_after>use std::io;
use std::io::fs::PathExtensions;
use std::io::process::Command;
use std::fmt::{Show, Formatter, Error};
use std::collections::HashSet;

use app_result::{AppResult, app_err};
use dependencies::SourceKind;

use dirs::{
   rusty_tags_cache_dir,
   cargo_git_src_dir,
   cargo_crates_io_src_dir,
   glob_path
};

pub struct Tags
{
   /// the root directory of the source code
   /// for which the tags have been created
   pub src_dir: Path,

   /// the tags file of the sources in `src_dir`
   pub tags_file: Path,

   /// indicates if the tags file is already existing
   /// and the cached tags file is returned
   pub cached: bool
}

/// Checks if there's already a tags file for `src_kind`
/// and if not it's creating a new tags file and returning it.
pub fn update_tags(src_kind: &SourceKind) -> AppResult<Tags>
{
   let cache_dir = try!(rusty_tags_cache_dir());

   let mut src_tags = cache_dir.clone();
   src_tags.push(src_kind.tags_file_name());

   let src_dir = try!(find_src_dir(src_kind));
   if src_tags.is_file() {
      return Ok(Tags { src_dir: src_dir, tags_file: src_tags, cached: true });
   }

   try!(create_tags(&src_dir, &src_tags));
   Ok(Tags { src_dir: src_dir, tags_file: src_tags, cached: false })
}

/// Does the same thing as `update_tags`, but also checks if the `lib.rs`
/// file of the library has public reexports of external crates. If
/// that's the case, then the tags of the public reexported external
/// crates are merged into the tags of the library.
pub fn update_tags_and_check_for_reexports(src_kind: &SourceKind, dependencies: &Vec<SourceKind>) -> AppResult<Tags>
{
   let lib_tags = try!(update_tags(src_kind));
   if lib_tags.cached {
      return Ok(lib_tags);
   }

   let reexp_crates = try!(find_reexported_crates(&lib_tags.src_dir));
   if reexp_crates.is_empty() {
      return Ok(lib_tags);
   }

   println!("Found public reexports in '{}' of:", src_kind.get_lib_name());
   for rcrate in reexp_crates.iter() {
      println!("   {}", rcrate);
   }
   println!("");

   let mut crate_tags = Vec::<Path>::new();
   for rcrate in reexp_crates.iter() {
      if let Some(crate_dep) = dependencies.iter().find(|d| d.get_lib_name() == *rcrate) {
         crate_tags.push(try!(update_tags(crate_dep)).tags_file.clone());
      }
   }

   if crate_tags.is_empty() {
      return Ok(lib_tags);
   }

   crate_tags.push(lib_tags.tags_file.clone());
   try!(merge_tags(&crate_tags, &lib_tags.tags_file));
   Ok(lib_tags)
}

/// merges `tag_files` into `into_tag_file`
pub fn merge_tags(tag_files: &Vec<Path>, into_tag_file: &Path) -> AppResult<()>
{
   println!("Merging ...\n   tags:");

   let mut file_contents: Vec<String> = Vec::new();
   for file in tag_files.iter() {
      println!("      {}", file.display());
      file_contents.push(try!(io::File::open(file).read_to_string()));
   }

   println!("\n   into:\n      {}\n", into_tag_file.display());

   let mut merged_lines: Vec<&str> = Vec::with_capacity(100_000);
   for content in file_contents.iter() {
      for line in content.as_slice().lines_any() {
         if ! line.is_empty() && line.char_at(0) != '!' {
            merged_lines.push(line);
         }
      }
   }

   merged_lines.sort();

   let mut tag_file = try!(io::File::open_mode(into_tag_file, io::Truncate, io::ReadWrite));
   try!(tag_file.write_line("!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;\" to lines/"));
   try!(tag_file.write_line("!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/"));

   for line in merged_lines.iter() {
      try!(tag_file.write_line(*line));
   }

   Ok(())
}

/// creates tags recursive for the directory hierarchy starting at `src_dir`
/// and writes them to `tags_file`
pub fn create_tags(src_dir: &Path, tags_file: &Path) -> AppResult<()>
{
   let mut cmd = Command::new("ctags");
   cmd.arg("--recurse")
      .arg("--langdef=Rust")
      .arg("--langmap=Rust:.rs")
      .arg("--regex-Rust=/^[ \\t]*(#\\[[^\\]]\\][ \\t]*)*(pub[ \\t]+)?(extern[ \\t]+)?(\"[^\"]+\"[ \\t]+)?(unsafe[ \\t]+)?fn[ \\t]+([a-zA-Z0-9_]+)/\\6/f,functions,function definitions/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?type[ \\t]+([a-zA-Z0-9_]+)/\\2/T,types,type definitions/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?enum[ \\t]+([a-zA-Z0-9_]+)/\\2/g,enum,enumeration names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?struct[ \\t]+([a-zA-Z0-9_]+)/\\2/s,structure names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?mod[ \\t]+([a-zA-Z0-9_]+)/\\2/m,modules,module names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?static[ \\t]+([a-zA-Z0-9_]+)/\\2/c,consts,static constants/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?trait[ \\t]+([a-zA-Z0-9_]+)/\\2/t,traits,traits/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?impl([ \\t\\n]*<[^>]*>)?[ \\t]+(([a-zA-Z0-9_:]+)[ \\t]*(<[^>]*>)?[ \\t]+(for)[ \\t]+)?([a-zA-Z0-9_]+)/\\4 \\6 \\7/i,impls,trait implementations/")
      .arg("--regex-Rust=/^[ \\t]*macro_rules![ \\t]+([a-zA-Z0-9_]+)/\\1/d,macros,macro definitions/")
      .arg("-o")
      .arg(tags_file)
      .arg(src_dir);

   println!("Creating tags ...\n   for source:\n      {}\n\n   cached at:\n      {}\n",
            src_dir.display(), tags_file.display());

   try!(cmd.output());
   Ok(())
}

/// find the source directory of `src_kind`, for git sources the directories
/// in `~/.cargo/git/checkouts` are considered and for crates.io sources
/// the directories in `~/.cargo/registry/src/github.com-*` are considered
fn find_src_dir(src_kind: &SourceKind) -> AppResult<Path>
{
   match *src_kind {
      SourceKind::Git { ref lib_name, ref commit_hash } => {
         let mut lib_src = lib_name.clone();
         lib_src.push_str("-*");

         let mut src_dir = try!(cargo_git_src_dir());
         src_dir.push(&lib_src);
         src_dir.push("master");

         let mut src_paths = glob_path(&src_dir);
         for src_path in src_paths {
            let src_commit_hash = try!(get_commit_hash(&src_path));
            if *commit_hash == src_commit_hash {
               return Ok(src_path);
            }
         }

         // the git repository name hasn't to match the name of the library,
         // so here we're just going through all git directories and searching
         // for the one with a matching commit hash
         let mut src_dir = try!(cargo_git_src_dir());
         src_dir.push("*");
         src_dir.push("master");

         let mut src_paths = glob_path(&src_dir);
         for src_path in src_paths {
            let src_commit_hash = try!(get_commit_hash(&src_path));
            if *commit_hash == src_commit_hash {
               return Ok(src_path);
            }
         }

         Err(app_err(format!("
   Couldn't find git repository of the dependency '{}'!
   Have you run 'cargo build' at least once or have you added/updated a dependency without calling 'cargo build' again?", lib_name)))
      },

      SourceKind::CratesIo { ref lib_name, ref version } => {
         let mut lib_src = lib_name.clone();
         lib_src.push('-');
         lib_src.push_str(&**version);

         let mut src_dir = try!(cargo_crates_io_src_dir());
         src_dir.push(&lib_src);

         if ! src_dir.is_dir() {
            return Err(app_err(format!("
   Couldn't find source code of the dependency '{}'!
   Have you run 'cargo build' at least once or have you added/updated a dependency without calling 'cargo build' again?", lib_name)))
         }

         Ok(src_dir)
      }
   }
}

type CrateName = String;

/// searches in the file `<src_dir>/src/lib.rs` for external crates
/// that are reexpored and returns their names
fn find_reexported_crates(src_dir: &Path) -> AppResult<Vec<CrateName>>
{
   let mut lib_file = src_dir.clone();
   lib_file.push("src");
   lib_file.push("lib.rs");

   if ! lib_file.is_file() {
      return Ok(Vec::new());
   }

   let contents = try!(io::File::open(&lib_file).read_to_string());
   let mut lines = contents.lines_any();

   type ModuleName = String;
   let mut pub_uses = HashSet::<ModuleName>::new();

   #[deriving(Eq, PartialEq, Hash)]
   struct ExternCrate<'a>
   {
      name: &'a str,
      as_name: &'a str
   }

   let mut extern_crates = HashSet::<ExternCrate>::new();

   for line in lines {
      let items = line.trim_chars(';').split(' ').collect::<Vec<&str>>();
      if items.len() < 3 {
         continue;
      }

      if items[0] == "pub" && items[1] == "use" {
         let mods = items[2].split_str("::").collect::<Vec<&str>>();
         if mods.len() >= 1 {
            pub_uses.insert(mods[0].to_string());
         }
      }

      if items[0] == "extern" && items[1] == "crate" {
         if items.len() == 3 {
            extern_crates.insert(ExternCrate { name: items[2].trim_chars('"'), as_name: items[2] });
         }
         else if items.len() == 5 && items[3] == "as" {
            extern_crates.insert(ExternCrate { name: items[2].trim_chars('"'), as_name: items[4] });
         }
      }
   }

   let mut reexp_crates = Vec::<CrateName>::new();
   for extern_crate in extern_crates.iter() {
      if pub_uses.contains(extern_crate.as_name) {
         reexp_crates.push(extern_crate.name.to_string());
      }
   }

   Ok(reexp_crates)
}

/// get the commit hash of the current `HEAD` of the git repository located at `git_dir`
fn get_commit_hash(git_dir: &Path) -> AppResult<String>
{
   let mut cmd = Command::new("git");
   cmd.cwd(git_dir)
      .arg("rev-parse")
      .arg("HEAD");

   let out = try!(cmd.output());
   String::from_utf8(out.output)
      .map(|s| s.as_slice().trim().to_string())
      .map_err(|_| app_err("Couldn't convert git output to utf8!".to_string()))
}

impl Show for Tags
{
   fn fmt(&self, f: &mut Formatter) -> Result<(), Error>
   {
      write!(f, "Tags ( src_dir: {}, tags_file: {}, cached: {} )",
             self.src_dir.display(), self.tags_file.display(), self.cached)
   }
}
<|endoftext|>


<commit_before>extern crate "rustc-serialize" as serialize;

use std::fmt::Debug;
use std::collections::HashMap;

use rustc_serialize::{
    Encoder,
    Decoder,
    Encodable,
    Decodable
};

use super::{
    encode,
    decode,
    decode_from,
    encoded_size,
    DecodingError,
    DecodingResult
};
use super::SizeLimit::{Infinite, Bounded};

fn the_same<'a, V>(element: V) where V: Encodable, V: Decodable, V: PartialEq, V: Debug {
    let size = encoded_size(&element);
    let encoded = encode(&element, Infinite).unwrap();
    let decoded = decode(&encoded[]).unwrap();
    assert!(element == decoded);
    assert!(size == encoded.len() as u64);
}

#[test]
fn test_numbers() {
    // unsigned positive
    the_same(5u8);
    the_same(5u16);
    the_same(5u32);
    the_same(5u64);
    // signed positive
    the_same(5i8);
    the_same(5i16);
    the_same(5i32);
    the_same(5i64);
    // signed negative
    the_same(-5i8);
    the_same(-5i16);
    the_same(-5i32);
    the_same(-5i64);
    // floating
    the_same(-100f32);
    the_same(0f32);
    the_same(5f32);
    the_same(-100f64);
    the_same(5f64);
}

#[test]
fn test_string() {
    the_same("".to_string());
    the_same("a".to_string());
}

#[test]
fn test_tuple() {
    the_same((1is,));
    the_same((1is,2is,3is));
    the_same((1is,"foo".to_string(),()));
}

#[test]
fn test_basic_struct() {
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    struct Easy {
        x: isize,
        s: String,
        y: usize
    }
    the_same(Easy{x: -4, s: "foo".to_string(), y: 10});
}

#[test]
fn test_nested_struct() {
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    struct Easy {
        x: isize,
        s: String,
        y: usize
    }
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    struct Nest {
        f: Easy,
        b: usize,
        s: Easy
    }

    the_same(Nest {
        f: Easy {x: -1, s: "foo".to_string(), y: 20},
        b: 100,
        s: Easy {x: -100, s: "bar".to_string(), y: 20}
    });
}

#[test]
fn test_struct_tuple() {
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    struct TubStr(usize, String, f32);

    the_same(TubStr(5, "hello".to_string(), 3.2));
}

#[test]
fn option() {
    the_same(Some(5us));
    the_same(Some("foo bar".to_string()));
    the_same(None::<usize>);
}

#[test]
fn enm() {
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    enum TestEnum {
        NoArg,
        OneArg(usize),
        AnotherNoArg
    }
    the_same(TestEnum::NoArg);
    the_same(TestEnum::OneArg(4));
    the_same(TestEnum::AnotherNoArg);
}

#[test]
fn struct_enum() {
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    enum TestEnum {
        NoArg,
        OneArg(usize),
        AnotherNoArg,
        StructLike{x: usize, y: f32}
    }
    the_same(TestEnum::NoArg);
    the_same(TestEnum::OneArg(4));
    the_same(TestEnum::AnotherNoArg);
    the_same(TestEnum::StructLike{x: 4, y: 3.14159});
    the_same(vec![TestEnum::NoArg, TestEnum::OneArg(5), TestEnum::AnotherNoArg,
                  TestEnum::StructLike{x: 4, y:1.4}]);
}

#[test]
fn many() {
    let v: Vec<u8> = vec![];
    the_same(v);
    the_same(vec![1u64]);
    the_same(vec![1u64,2,3,4,5,6]);
}

#[test]
fn map(){
    let mut m = HashMap::new();
    m.insert(4u64, "foo".to_string());
    m.insert(0u64, "bar".to_string());
    the_same(m);
}

#[test]
fn boole(){
    the_same(true);
    the_same(false);
}

#[test]
fn unicode() {
    the_same("å".to_string());
    the_same("aåååååååa".to_string());
}

#[test]
fn decoding_errors() {
    fn is_invalid_encoding<T>(res: DecodingResult<T>) {
        match res {
            Ok(_) => panic!("Expecting error"),
            Err(DecodingError::IoError(_)) => panic!("Expecting InvalidEncoding"),
            Err(DecodingError::SizeLimit) => panic!("Expecting InvalidEncoding"),
            Err(DecodingError::InvalidEncoding(_)) => {},
        }
    }

    is_invalid_encoding(decode::<bool>(vec![0xA].as_slice()));
    is_invalid_encoding(decode::<String>(vec![0, 0, 0, 0, 0, 0, 0, 1, 0xFF].as_slice()));
    // Out-of-bounds variant
    #[derive(RustcEncodable, RustcDecodable)]
    enum Test {
        One,
        Two,
    };
    is_invalid_encoding(decode::<Test>(vec![0, 0, 0, 5].as_slice()));
    is_invalid_encoding(decode::<Option<u8>>(vec![5, 0].as_slice()));
}

#[test]
fn too_big_decode() {
    let encoded = vec![0,0,0,3];
    let mut encoded_ref = encoded.as_slice();
    let decoded: Result<u32, _> = decode_from(&mut encoded_ref, Bounded(3));
    assert!(decoded.is_err());

    let encoded = vec![0,0,0,3];
    let mut encoded_ref = encoded.as_slice();
    let decoded: Result<u32, _> = decode_from(&mut encoded_ref, Bounded(4));
    assert!(decoded.is_ok());
}

#[test]
fn too_big_char_decode() {
    let encoded = vec![0x41];
    let mut encoded_ref = encoded.as_slice();
    let decoded: Result<char, _> = decode_from(&mut encoded_ref, Bounded(1));
    assert_eq!(decoded, Ok('A'));
}

#[test]
fn too_big_encode() {
    assert!(encode(&0u32, Bounded(3)).is_err());
    assert!(encode(&0u32, Bounded(4)).is_ok());

    assert!(encode(&"abcde", Bounded(8 + 4)).is_err());
    assert!(encode(&"abcde", Bounded(8 + 5)).is_ok());
}

#[test]
fn test_encoded_size() {
    assert!(encoded_size(&0u8) == 1);
    assert!(encoded_size(&0u16) == 2);
    assert!(encoded_size(&0u32) == 4);
    assert!(encoded_size(&0u64) == 8);

    // length
    assert!(encoded_size(&"") == 8);
    assert!(encoded_size(&"a") == 8 + 1);

    assert!(encoded_size(&vec![0u32, 1u32, 2u32]) == 8 + 3 * (4))
}

#[test]
fn encode_box() {
    the_same(Box::new(5));
}
<commit_msg>clean up tests<commit_after>extern crate "rustc-serialize" as serialize;

use std::fmt::Debug;
use std::collections::HashMap;

use rustc_serialize::{
    Encoder,
    Decoder,
    Encodable,
    Decodable
};

use super::{
    encode,
    decode,
    decode_from,
    encoded_size,
    DecodingError,
    DecodingResult
};
use super::SizeLimit::{Infinite, Bounded};

fn the_same<V>(element: V)
where V: Encodable + Decodable + PartialEq + Debug {
    let size = encoded_size(&element);
    let encoded = encode(&element, Infinite).unwrap();
    let decoded = decode(&encoded[]).unwrap();
    assert!(element == decoded);
    assert!(size == encoded.len() as u64);
}

#[test]
fn test_numbers() {
    // unsigned positive
    the_same(5u8);
    the_same(5u16);
    the_same(5u32);
    the_same(5u64);
    // signed positive
    the_same(5i8);
    the_same(5i16);
    the_same(5i32);
    the_same(5i64);
    // signed negative
    the_same(-5i8);
    the_same(-5i16);
    the_same(-5i32);
    the_same(-5i64);
    // floating
    the_same(-100f32);
    the_same(0f32);
    the_same(5f32);
    the_same(-100f64);
    the_same(5f64);
}

#[test]
fn test_string() {
    the_same("".to_string());
    the_same("a".to_string());
}

#[test]
fn test_tuple() {
    the_same((1is,));
    the_same((1is,2is,3is));
    the_same((1is,"foo".to_string(),()));
}

#[test]
fn test_basic_struct() {
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    struct Easy {
        x: isize,
        s: String,
        y: usize
    }
    the_same(Easy{x: -4, s: "foo".to_string(), y: 10});
}

#[test]
fn test_nested_struct() {
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    struct Easy {
        x: isize,
        s: String,
        y: usize
    }
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    struct Nest {
        f: Easy,
        b: usize,
        s: Easy
    }

    the_same(Nest {
        f: Easy {x: -1, s: "foo".to_string(), y: 20},
        b: 100,
        s: Easy {x: -100, s: "bar".to_string(), y: 20}
    });
}

#[test]
fn test_struct_tuple() {
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    struct TubStr(usize, String, f32);

    the_same(TubStr(5, "hello".to_string(), 3.2));
}

#[test]
fn option() {
    the_same(Some(5us));
    the_same(Some("foo bar".to_string()));
    the_same(None::<usize>);
}

#[test]
fn enm() {
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    enum TestEnum {
        NoArg,
        OneArg(usize),
        AnotherNoArg
    }
    the_same(TestEnum::NoArg);
    the_same(TestEnum::OneArg(4));
    the_same(TestEnum::AnotherNoArg);
}

#[test]
fn struct_enum() {
    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    enum TestEnum {
        NoArg,
        OneArg(usize),
        AnotherNoArg,
        StructLike{x: usize, y: f32}
    }
    the_same(TestEnum::NoArg);
    the_same(TestEnum::OneArg(4));
    the_same(TestEnum::AnotherNoArg);
    the_same(TestEnum::StructLike{x: 4, y: 3.14159});
    the_same(vec![TestEnum::NoArg, TestEnum::OneArg(5), TestEnum::AnotherNoArg,
                  TestEnum::StructLike{x: 4, y:1.4}]);
}

#[test]
fn many() {
    let v: Vec<u8> = vec![];
    the_same(v);
    the_same(vec![1u64]);
    the_same(vec![1u64,2,3,4,5,6]);
}

#[test]
fn map(){
    let mut m = HashMap::new();
    m.insert(4u64, "foo".to_string());
    m.insert(0u64, "bar".to_string());
    the_same(m);
}

#[test]
fn boole(){
    the_same(true);
    the_same(false);
}

#[test]
fn unicode() {
    the_same("å".to_string());
    the_same("aåååååååa".to_string());
}

#[test]
fn decoding_errors() {
    fn is_invalid_encoding<T>(res: DecodingResult<T>) {
        match res {
            Ok(_) => panic!("Expecting error"),
            Err(DecodingError::IoError(_)) => panic!("Expecting InvalidEncoding"),
            Err(DecodingError::SizeLimit) => panic!("Expecting InvalidEncoding"),
            Err(DecodingError::InvalidEncoding(_)) => {},
        }
    }

    is_invalid_encoding(decode::<bool>(vec![0xA].as_slice()));
    is_invalid_encoding(decode::<String>(vec![0, 0, 0, 0, 0, 0, 0, 1, 0xFF].as_slice()));
    // Out-of-bounds variant
    #[derive(RustcEncodable, RustcDecodable)]
    enum Test {
        One,
        Two,
    };
    is_invalid_encoding(decode::<Test>(vec![0, 0, 0, 5].as_slice()));
    is_invalid_encoding(decode::<Option<u8>>(vec![5, 0].as_slice()));
}

#[test]
fn too_big_decode() {
    let encoded = vec![0,0,0,3];
    let mut encoded_ref = encoded.as_slice();
    let decoded: Result<u32, _> = decode_from(&mut encoded_ref, Bounded(3));
    assert!(decoded.is_err());

    let encoded = vec![0,0,0,3];
    let mut encoded_ref = encoded.as_slice();
    let decoded: Result<u32, _> = decode_from(&mut encoded_ref, Bounded(4));
    assert!(decoded.is_ok());
}

#[test]
fn too_big_char_decode() {
    let encoded = vec![0x41];
    let mut encoded_ref = encoded.as_slice();
    let decoded: Result<char, _> = decode_from(&mut encoded_ref, Bounded(1));
    assert_eq!(decoded, Ok('A'));
}

#[test]
fn too_big_encode() {
    assert!(encode(&0u32, Bounded(3)).is_err());
    assert!(encode(&0u32, Bounded(4)).is_ok());

    assert!(encode(&"abcde", Bounded(8 + 4)).is_err());
    assert!(encode(&"abcde", Bounded(8 + 5)).is_ok());
}

#[test]
fn test_encoded_size() {
    assert!(encoded_size(&0u8) == 1);
    assert!(encoded_size(&0u16) == 2);
    assert!(encoded_size(&0u32) == 4);
    assert!(encoded_size(&0u64) == 8);

    // length is stored as u64
    assert!(encoded_size(&"") == 8);
    assert!(encoded_size(&"a") == 8 + 1);

    assert!(encoded_size(&vec![0u32, 1u32, 2u32]) == 8 + 3 * (4))
}

#[test]
fn encode_box() {
    the_same(Box::new(5));
}
<|endoftext|>


<commit_before>//! TopK implementation.
use crate::countminsketch::CountMinSketch;
use std::cmp::Ordering;
use std::collections::hash_map::Entry;
use std::collections::{BTreeSet, HashMap};
use std::fmt;
use std::hash::Hash;
use std::rc::Rc;

#[derive(Debug)]
struct TreeEntry<T>
where
    T: Eq + Ord,
{
    obj: Rc<T>,
    n: usize,
}

impl<T> Clone for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn clone(&self) -> Self {
        TreeEntry {
            obj: Rc::clone(&self.obj),
            n: self.n,
        }
    }
}

impl<T> PartialEq for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn eq(&self, other: &TreeEntry<T>) -> bool {
        self.obj == other.obj
    }
}

impl<T> Eq for TreeEntry<T> where T: Eq + Ord {}

impl<T> PartialOrd for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn partial_cmp(&self, other: &TreeEntry<T>) -> Option<Ordering> {
        match self.n.cmp(&other.n) {
            Ordering::Greater => Some(Ordering::Greater),
            Ordering::Less => Some(Ordering::Less),
            Ordering::Equal => Some(self.obj.cmp(&other.obj)),
        }
    }
}

impl<T> Ord for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn cmp(&self, other: &TreeEntry<T>) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

/// A TopK is a data structure keeps the `k` most frequent data points of a stream.
///
/// # Examples
/// ```
/// use pdatastructs::countminsketch::CountMinSketch;
/// use pdatastructs::topk::TopK;
///
/// // set up filter
/// let epsilon = 0.1;  // error threshold
/// let delta = 0.2;  // epsilon is hit in (1 - 0.2) * 100% = 80%
/// let mut cms = CountMinSketch::with_point_query_properties(epsilon, delta);
/// let mut tk = TopK::new(2, cms);
///
/// // add some data
/// for i in 0..1000 {
///     let x = i % 10;
///     let y = if x < 4 {
///         0
///     } else if x < 7 {
///         1
///     } else {
///         x
///     };
///     tk.add(y);
/// }
///
/// // later
/// let elements: Vec<u64> = tk.iter().collect();
/// assert_eq!(elements, vec![1, 0]);
/// ```
///
/// # Applications
/// - getting a fixed number of most common example of a large data set
/// - detect hotspots / very common events
///
/// # How It Works
/// The data structure is based on two parts:
///
/// 1. a reservoir for the most common elements that also stores precise (but not exact) counter
///    data
/// 2. a CountMinSketch to guess the count of all other elements
///
/// On insert, it is checked if the element is already in the reservoir. If so, the precise count
/// is updated. If not, the CountMinSketch is queried for a guess. If the guess is higher than the
/// least common element in the reservoir, the new element displaces this reservoir element. The
/// guess is used as an exact count and is stored alongside. In any case, the CountMinSketch is
/// updated.
///
/// # See Also
/// - `std::vec::Vec`: exact solution but needs to store the entire data stream
///
/// # References
/// - ["Top K Frequent Items Algorithm", Zhipeng Jiang, 2017](https://zpjiang.me/2017/11/13/top-k-elementes-system-design/)
#[derive(Clone)]
pub struct TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    cms: CountMinSketch<T>,
    obj2count: HashMap<Rc<T>, usize>,
    tree: BTreeSet<TreeEntry<T>>,
    k: usize,
}

impl<T> TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    /// Create new Top-K data structure.
    ///
    /// - `k`: number of elements to remember
    /// - `cms`: CountMinSketch that is used for guessing the frequency of elements not currently
    ///   hold. Refer to its documentation about parameter selection.
    ///
    /// Panics if `k == 0`.
    pub fn new(k: usize, cms: CountMinSketch<T>) -> Self {
        assert!(k > 0, "k must be greater than 0");

        Self {
            cms,
            obj2count: HashMap::new(),
            tree: BTreeSet::new(),
            k,
        }
    }

    /// Number of data points to remember.
    pub fn k(&self) -> usize {
        self.k
    }

    /// Observe a data point.
    pub fn add(&mut self, obj: T) {
        // create Rc so we can use obj in multiple data structs
        let rc = Rc::new(obj);

        // always increase CountMinSketch counts
        self.cms.add(&rc);

        // check if entry is in top K
        let size = self.obj2count.len();
        let mut update_obj2count: Option<(usize, Rc<T>)> = None;
        match self.obj2count.entry(Rc::clone(&rc)) {
            Entry::Occupied(mut o) => {
                // it is => increase exact counter
                let n = o.get_mut();
                *n += 1;

                // update tree data
                let mut entry = TreeEntry {
                    obj: Rc::clone(&rc),
                    n: *n - 1,
                };
                self.tree.remove(&entry);
                entry.n += 1;
                self.tree.insert(entry);
            }
            Entry::Vacant(v) => {
                // it's not => check capicity
                if size < self.k {
                    // space left => add to top k
                    v.insert(1);
                    self.tree.insert(TreeEntry {
                        obj: Rc::clone(&rc),
                        n: 1,
                    });
                } else {
                    // not enough space => check if it would be a top k element

                    let count = self.cms.query_point(&rc);
                    // count at this point already contains the +1 from the current insertions
                    // because we've updated the CountMinSketch before the query

                    let min: TreeEntry<T> = (*self.tree.iter().next().unwrap()).clone();
                    if count > min.n {
                        // => kick out minimal element of top k
                        self.tree.remove(&min);
                        self.tree.insert(TreeEntry {
                            obj: Rc::clone(&rc),
                            n: count,
                        });

                        // delay obj2count changes, because it is currently borrowed
                        update_obj2count = Some((count, min.obj));
                    }
                }
            }
        }

        // trick the borrow checker
        if let Some((count, obj)) = update_obj2count {
            self.obj2count.insert(rc, count);
            self.obj2count.remove(&obj);
        }
    }

    /// Iterates over collected top-k values.
    ///
    /// The result may contain less than `k` values if less than `k` unique data points where
    /// observed.
    pub fn iter<'a>(&'a self) -> impl 'a + Iterator<Item = T> {
        self.tree.iter().map(|x| (*x.obj).clone())
    }

    /// Check whether the sampler is empty (i.e. no data points observer so far)
    pub fn is_empty(&self) -> bool {
        self.obj2count.is_empty()
    }

    /// Clear sampler, so that it behaves like it never observed any data points.
    pub fn clear(&mut self) {
        self.cms.clear();
        self.tree.clear();
        self.obj2count.clear();
    }
}

impl<T> fmt::Debug for TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TopK {{ k: {} }}", self.k)
    }
}

impl<T> Extend<T> for TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    fn extend<S: IntoIterator<Item = T>>(&mut self, iter: S) {
        for elem in iter {
            self.add(elem);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::TopK;
    use crate::countminsketch::CountMinSketch;

    #[test]
    #[should_panic(expected = "k must be greater than 0")]
    fn new_panics_k0() {
        let cms = CountMinSketch::with_params(10, 20);
        TopK::<usize>::new(0, cms);
    }

    #[test]
    fn getter() {
        let cms = CountMinSketch::with_params(10, 20);
        let tk: TopK<usize> = TopK::new(2, cms);

        assert_eq!(tk.k(), 2);
    }

    #[test]
    fn add_1() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![1]);
    }

    #[test]
    fn add_2_same() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        tk.add(1);
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![1]);
    }

    #[test]
    fn add_2_different() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        tk.add(2);
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![1, 2]);
    }

    #[test]
    fn add_n() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        for i in 0..5 {
            tk.add(i);
        }
        for _ in 0..100 {
            tk.add(99);
        }
        for _ in 0..100 {
            tk.add(100);
        }
        for i in 0..5 {
            tk.add(i);
        }
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![99, 100]);
    }

    #[test]
    fn is_empty() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);
        assert_eq!(tk.is_empty(), true);

        tk.add(0);
        assert_eq!(tk.is_empty(), false);
    }

    #[test]
    fn clear() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);
        tk.add(0);

        tk.clear();
        assert_eq!(tk.is_empty(), true);

        tk.add(1);
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![1]);
    }

    #[test]
    fn clone() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk1 = TopK::new(2, cms);
        tk1.add(0);

        let mut tk2 = tk1.clone();
        tk2.add(1);

        assert_eq!(tk1.iter().collect::<Vec<u32>>(), vec![0]);
        assert_eq!(tk2.iter().collect::<Vec<u32>>(), vec![0, 1]);
    }

    #[test]
    fn debug() {
        let cms = CountMinSketch::with_params(10, 20);
        let tk: TopK<usize> = TopK::new(2, cms);
        assert_eq!(format!("{:?}", tk), "TopK { k: 2 }");
    }

    #[test]
    fn extend() {
        let cms = CountMinSketch::with_params(10, 10);
        let mut tk = TopK::new(2, cms);

        tk.extend(vec![0, 1]);
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![0, 1]);
    }
}
<commit_msg>topk: use return value from cms.add<commit_after>//! TopK implementation.
use crate::countminsketch::CountMinSketch;
use std::cmp::Ordering;
use std::collections::hash_map::Entry;
use std::collections::{BTreeSet, HashMap};
use std::fmt;
use std::hash::Hash;
use std::rc::Rc;

#[derive(Debug)]
struct TreeEntry<T>
where
    T: Eq + Ord,
{
    obj: Rc<T>,
    n: usize,
}

impl<T> Clone for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn clone(&self) -> Self {
        TreeEntry {
            obj: Rc::clone(&self.obj),
            n: self.n,
        }
    }
}

impl<T> PartialEq for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn eq(&self, other: &TreeEntry<T>) -> bool {
        self.obj == other.obj
    }
}

impl<T> Eq for TreeEntry<T> where T: Eq + Ord {}

impl<T> PartialOrd for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn partial_cmp(&self, other: &TreeEntry<T>) -> Option<Ordering> {
        match self.n.cmp(&other.n) {
            Ordering::Greater => Some(Ordering::Greater),
            Ordering::Less => Some(Ordering::Less),
            Ordering::Equal => Some(self.obj.cmp(&other.obj)),
        }
    }
}

impl<T> Ord for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn cmp(&self, other: &TreeEntry<T>) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

/// A TopK is a data structure keeps the `k` most frequent data points of a stream.
///
/// # Examples
/// ```
/// use pdatastructs::countminsketch::CountMinSketch;
/// use pdatastructs::topk::TopK;
///
/// // set up filter
/// let epsilon = 0.1;  // error threshold
/// let delta = 0.2;  // epsilon is hit in (1 - 0.2) * 100% = 80%
/// let mut cms = CountMinSketch::with_point_query_properties(epsilon, delta);
/// let mut tk = TopK::new(2, cms);
///
/// // add some data
/// for i in 0..1000 {
///     let x = i % 10;
///     let y = if x < 4 {
///         0
///     } else if x < 7 {
///         1
///     } else {
///         x
///     };
///     tk.add(y);
/// }
///
/// // later
/// let elements: Vec<u64> = tk.iter().collect();
/// assert_eq!(elements, vec![1, 0]);
/// ```
///
/// # Applications
/// - getting a fixed number of most common example of a large data set
/// - detect hotspots / very common events
///
/// # How It Works
/// The data structure is based on two parts:
///
/// 1. a reservoir for the most common elements that also stores precise (but not exact) counter
///    data
/// 2. a CountMinSketch to guess the count of all other elements
///
/// On insert, it is checked if the element is already in the reservoir. If so, the precise count
/// is updated. If not, the CountMinSketch is queried for a guess. If the guess is higher than the
/// least common element in the reservoir, the new element displaces this reservoir element. The
/// guess is used as an exact count and is stored alongside. In any case, the CountMinSketch is
/// updated.
///
/// # See Also
/// - `std::vec::Vec`: exact solution but needs to store the entire data stream
///
/// # References
/// - ["Top K Frequent Items Algorithm", Zhipeng Jiang, 2017](https://zpjiang.me/2017/11/13/top-k-elementes-system-design/)
#[derive(Clone)]
pub struct TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    cms: CountMinSketch<T>,
    obj2count: HashMap<Rc<T>, usize>,
    tree: BTreeSet<TreeEntry<T>>,
    k: usize,
}

impl<T> TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    /// Create new Top-K data structure.
    ///
    /// - `k`: number of elements to remember
    /// - `cms`: CountMinSketch that is used for guessing the frequency of elements not currently
    ///   hold. Refer to its documentation about parameter selection.
    ///
    /// Panics if `k == 0`.
    pub fn new(k: usize, cms: CountMinSketch<T>) -> Self {
        assert!(k > 0, "k must be greater than 0");

        Self {
            cms,
            obj2count: HashMap::new(),
            tree: BTreeSet::new(),
            k,
        }
    }

    /// Number of data points to remember.
    pub fn k(&self) -> usize {
        self.k
    }

    /// Observe a data point.
    pub fn add(&mut self, obj: T) {
        // create Rc so we can use obj in multiple data structs
        let rc = Rc::new(obj);

        // always increase CountMinSketch counts
        let count = self.cms.add(&rc);

        // check if entry is in top K
        let size = self.obj2count.len();
        let mut update_obj2count: Option<(usize, Rc<T>)> = None;
        match self.obj2count.entry(Rc::clone(&rc)) {
            Entry::Occupied(mut o) => {
                // it is => increase exact counter
                let n = o.get_mut();
                *n += 1;

                // update tree data
                let mut entry = TreeEntry {
                    obj: Rc::clone(&rc),
                    n: *n - 1,
                };
                self.tree.remove(&entry);
                entry.n += 1;
                self.tree.insert(entry);
            }
            Entry::Vacant(v) => {
                // it's not => check capicity
                if size < self.k {
                    // space left => add to top k
                    debug_assert!(count == 1);
                    v.insert(1);
                    self.tree.insert(TreeEntry {
                        obj: Rc::clone(&rc),
                        n: 1,
                    });
                } else {
                    // not enough space => check if it would be a top k element

                    // count at this point already contains the +1 from the current insertions
                    // because we've updated the CountMinSketch before the query

                    let min: TreeEntry<T> = (*self.tree.iter().next().unwrap()).clone();
                    if count > min.n {
                        // => kick out minimal element of top k
                        self.tree.remove(&min);
                        self.tree.insert(TreeEntry {
                            obj: Rc::clone(&rc),
                            n: count,
                        });

                        // delay obj2count changes, because it is currently borrowed
                        update_obj2count = Some((count, min.obj));
                    }
                }
            }
        }

        // trick the borrow checker
        if let Some((count, obj)) = update_obj2count {
            self.obj2count.insert(rc, count);
            self.obj2count.remove(&obj);
        }
    }

    /// Iterates over collected top-k values.
    ///
    /// The result may contain less than `k` values if less than `k` unique data points where
    /// observed.
    pub fn iter<'a>(&'a self) -> impl 'a + Iterator<Item = T> {
        self.tree.iter().map(|x| (*x.obj).clone())
    }

    /// Check whether the sampler is empty (i.e. no data points observer so far)
    pub fn is_empty(&self) -> bool {
        self.obj2count.is_empty()
    }

    /// Clear sampler, so that it behaves like it never observed any data points.
    pub fn clear(&mut self) {
        self.cms.clear();
        self.tree.clear();
        self.obj2count.clear();
    }
}

impl<T> fmt::Debug for TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TopK {{ k: {} }}", self.k)
    }
}

impl<T> Extend<T> for TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    fn extend<S: IntoIterator<Item = T>>(&mut self, iter: S) {
        for elem in iter {
            self.add(elem);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::TopK;
    use crate::countminsketch::CountMinSketch;

    #[test]
    #[should_panic(expected = "k must be greater than 0")]
    fn new_panics_k0() {
        let cms = CountMinSketch::with_params(10, 20);
        TopK::<usize>::new(0, cms);
    }

    #[test]
    fn getter() {
        let cms = CountMinSketch::with_params(10, 20);
        let tk: TopK<usize> = TopK::new(2, cms);

        assert_eq!(tk.k(), 2);
    }

    #[test]
    fn add_1() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![1]);
    }

    #[test]
    fn add_2_same() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        tk.add(1);
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![1]);
    }

    #[test]
    fn add_2_different() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        tk.add(2);
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![1, 2]);
    }

    #[test]
    fn add_n() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        for i in 0..5 {
            tk.add(i);
        }
        for _ in 0..100 {
            tk.add(99);
        }
        for _ in 0..100 {
            tk.add(100);
        }
        for i in 0..5 {
            tk.add(i);
        }
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![99, 100]);
    }

    #[test]
    fn is_empty() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);
        assert_eq!(tk.is_empty(), true);

        tk.add(0);
        assert_eq!(tk.is_empty(), false);
    }

    #[test]
    fn clear() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk = TopK::new(2, cms);
        tk.add(0);

        tk.clear();
        assert_eq!(tk.is_empty(), true);

        tk.add(1);
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![1]);
    }

    #[test]
    fn clone() {
        let cms = CountMinSketch::with_params(10, 20);
        let mut tk1 = TopK::new(2, cms);
        tk1.add(0);

        let mut tk2 = tk1.clone();
        tk2.add(1);

        assert_eq!(tk1.iter().collect::<Vec<u32>>(), vec![0]);
        assert_eq!(tk2.iter().collect::<Vec<u32>>(), vec![0, 1]);
    }

    #[test]
    fn debug() {
        let cms = CountMinSketch::with_params(10, 20);
        let tk: TopK<usize> = TopK::new(2, cms);
        assert_eq!(format!("{:?}", tk), "TopK { k: 2 }");
    }

    #[test]
    fn extend() {
        let cms = CountMinSketch::with_params(10, 10);
        let mut tk = TopK::new(2, cms);

        tk.extend(vec![0, 1]);
        assert_eq!(tk.iter().collect::<Vec<u32>>(), vec![0, 1]);
    }
}
<|endoftext|>


<commit_before>use postgres::Connection;
use postgres_array::Array;

#[derive(Debug)]
pub struct User {
  pub id:              i32,
  pub first_name:      String,
  pub last_name:       String,
  pub headline:        String,
  pub work_roles:      Vec<String>,
  pub work_languages:  Vec<String>,
  pub work_experience: String,
  pub avatar_url:      String
}

impl User {
  pub fn find(conn: &Connection, id: &i32) -> Option<User> {
    let response = &conn.query(
      "SELECT * FROM users
        INNER JOIN talents ON users.id = talents.id
        WHERE users.id = $1
        LIMIT 1", &[&id]
    ).unwrap();

    let mut results = response.iter().map(|row| {
      let work_roles:     Array<String> = row.get("work_roles");
      let work_languages: Array<String> = row.get("work_languages");

      User {
        id:              row.get("id"),
        first_name:      row.get("firstname"),
        last_name:       row.get("lastname"),
        headline:        row.get("headline"),
        work_roles:      work_roles.iter().cloned().collect::<Vec<String>>(),
        work_languages:  work_languages.iter().cloned().collect::<Vec<String>>(),
        work_experience: row.get("work_experience"),
        avatar_url:      row.get("avatar_url"),
      }
    }).collect::<Vec<User>>();

    results.pop()
  }
}
<commit_msg>user: make members of User in optionals<commit_after>use postgres::Connection;
use postgres_array::Array;

#[derive(Debug)]
pub struct User {
  pub id:              i32,
  pub first_name:      Option<String>,
  pub last_name:       Option<String>,
  pub headline:        Option<String>,
  pub work_roles:      Vec<String>,
  pub work_languages:  Vec<String>,
  pub work_experience: Option<String>,
  pub avatar_url:      Option<String>
}

impl User {
  pub fn find(conn: &Connection, id: &i32) -> Option<User> {
    let response = &conn.query(
      "SELECT * FROM users
        INNER JOIN talents ON users.id = talents.id
        WHERE users.id = $1
        LIMIT 1", &[&id]
    ).unwrap();

    let mut results = response.iter().map(|row| {
      let work_roles:     Array<String> = row.get("work_roles");
      let work_languages: Array<String> = row.get("work_languages");

      User {
        id:              row.get("id"),
        first_name:      row.get("firstname"),
        last_name:       row.get("lastname"),
        headline:        row.get("headline"),
        work_roles:      work_roles.iter().cloned().collect::<Vec<String>>(),
        work_languages:  work_languages.iter().cloned().collect::<Vec<String>>(),
        work_experience: row.get("work_experience"),
        avatar_url:      row.get("avatar_url"),
      }
    }).collect::<Vec<User>>();

    results.pop()
  }
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

use futures::{Future, Poll};
use futures::stream::Stream;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{fmt, io};
use std::error::Error;
use std::net::{SocketAddr, ToSocketAddrs};

/// A bottom type that impls `Error`, `Serialize`, and `Deserialize`. It is impossible to
/// instantiate this type.
#[allow(unreachable_code)]
pub struct Never(!);

impl fmt::Debug for Never {
    fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
        unreachable!("Never cannot be instantiated");
    }
}

impl Error for Never {
    fn description(&self) -> &str {
        match self.0 {
        }
    }
}

impl fmt::Display for Never {
    fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
        }
    }
}

impl Future for Never {
    type Item = Never;
    type Error = Never;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.0 {
        }
    }
}

impl Stream for Never {
    type Item = Never;
    type Error = Never;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match self.0 {
        }
    }
}

impl Serialize for Never {
    fn serialize<S>(&self, _: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        match self.0 {
        }
    }
}

// Please don't try to deserialize this. :(
impl Deserialize for Never {
    fn deserialize<D>(_: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        panic!("Never cannot be instantiated!");
    }
}

/// A `String` that impls `std::error::Error`. Useful for quick-and-dirty error propagation.
#[derive(Debug, Serialize, Deserialize)]
pub struct Message(pub String);

impl Error for Message {
    fn description(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for Message {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl<S: Into<String>> From<S> for Message {
    fn from(s: S) -> Self {
        Message(s.into())
    }
}

/// Provides a utility method for more ergonomically parsing a `SocketAddr` when only one is
/// needed.
pub trait FirstSocketAddr: ToSocketAddrs {
    /// Returns the first resolved `SocketAddr`, if one exists.
    fn try_first_socket_addr(&self) -> io::Result<SocketAddr> {
        if let Some(a) = self.to_socket_addrs()?.next() {
            Ok(a)
        } else {
            Err(io::Error::new(io::ErrorKind::AddrNotAvailable,
                               "`ToSocketAddrs::to_socket_addrs` returned an empty iterator."))
        }
    }

    /// Returns the first resolved `SocketAddr` or panics otherwise.
    fn first_socket_addr(&self) -> SocketAddr {
        self.try_first_socket_addr().unwrap()
    }
}

impl<A: ToSocketAddrs> FirstSocketAddr for A {}

/// A struct that will format as the contained type if the type impls Debug.
pub struct Debugger<'a, T: 'a>(pub &'a T);

impl<'a, T: fmt::Debug> fmt::Debug for Debugger<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{:?}", self.0)
    }
}

impl<'a, T> fmt::Debug for Debugger<'a, T> {
    default fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{{not debuggable}}")
    }
}
<commit_msg>Simplify Never impls: return the inner uninhabited type for fns taking &self.<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

use futures::{Future, Poll};
use futures::stream::Stream;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{fmt, io};
use std::error::Error;
use std::net::{SocketAddr, ToSocketAddrs};

/// A bottom type that impls `Error`, `Serialize`, and `Deserialize`. It is impossible to
/// instantiate this type.
#[allow(unreachable_code)]
pub struct Never(!);

impl fmt::Debug for Never {
    fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
        self.0
    }
}

impl Error for Never {
    fn description(&self) -> &str {
        self.0
    }
}

impl fmt::Display for Never {
    fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
        self.0
    }
}

impl Future for Never {
    type Item = Never;
    type Error = Never;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.0
    }
}

impl Stream for Never {
    type Item = Never;
    type Error = Never;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        self.0
    }
}

impl Serialize for Never {
    fn serialize<S>(&self, _: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        self.0
    }
}

// Please don't try to deserialize this. :(
impl Deserialize for Never {
    fn deserialize<D>(_: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        panic!("Never cannot be instantiated!");
    }
}

/// A `String` that impls `std::error::Error`. Useful for quick-and-dirty error propagation.
#[derive(Debug, Serialize, Deserialize)]
pub struct Message(pub String);

impl Error for Message {
    fn description(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for Message {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl<S: Into<String>> From<S> for Message {
    fn from(s: S) -> Self {
        Message(s.into())
    }
}

/// Provides a utility method for more ergonomically parsing a `SocketAddr` when only one is
/// needed.
pub trait FirstSocketAddr: ToSocketAddrs {
    /// Returns the first resolved `SocketAddr`, if one exists.
    fn try_first_socket_addr(&self) -> io::Result<SocketAddr> {
        if let Some(a) = self.to_socket_addrs()?.next() {
            Ok(a)
        } else {
            Err(io::Error::new(io::ErrorKind::AddrNotAvailable,
                               "`ToSocketAddrs::to_socket_addrs` returned an empty iterator."))
        }
    }

    /// Returns the first resolved `SocketAddr` or panics otherwise.
    fn first_socket_addr(&self) -> SocketAddr {
        self.try_first_socket_addr().unwrap()
    }
}

impl<A: ToSocketAddrs> FirstSocketAddr for A {}

/// A struct that will format as the contained type if the type impls Debug.
pub struct Debugger<'a, T: 'a>(pub &'a T);

impl<'a, T: fmt::Debug> fmt::Debug for Debugger<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{:?}", self.0)
    }
}

impl<'a, T> fmt::Debug for Debugger<'a, T> {
    default fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{{not debuggable}}")
    }
}
<|endoftext|>


<commit_before>/// Validates an over-the-air callsign. APRS-IS is more forgiving.
///
/// Verifies that a valid callsign is valid:
/// >>> valid_callsign("W2GMD-1")
/// true
///
/// Verifies that an invalid callsign is invalid:
/// >>> valid_callsign("BURRITOS-99")
/// false
///
/// :param callsign: Callsign to validate.
/// :type callsign: str
///
/// :returns: True if valid, False otherwise.
/// :rtype: bool
pub fn valid_callsign(callsign: &str) -> bool {
    let mut callsign: &str = callsign.trim_matches(|x| x == '*' || (x as char).is_whitespace());
    let mut ssid: &str = "0";

    // Ensure single separator between callsign and SSID
    match callsign.matches('-').count() {
        0 => {},
        1 => {
            let call_components: Vec<&str> = callsign.split('-').collect();
            callsign = call_components[0];
            ssid = call_components[1];
        },
        _ => {return false;},
    }
    // Validate callsign
    match callsign.len() {
        4...6 => {},
        _ => {return false;},
    }
    for chr in callsign.chars() {
        if !chr.is_alphanumeric() {
            return false;
        }
    }
    // Validate SSID
    match ssid.len() {
        1...2 => {},
        _ => {return false;}
    }
    for chr in ssid.chars() {
        if !chr.is_numeric() {
            return false;
        }
    }
    match ssid.parse::<usize>()  {
        Err(e) => {return false;},
        Ok(s) => {
            match s {
                0...15 => {},
                _ => {return false;},
            }
        },
    }
    // Return
    true
}
<commit_msg>Docs for util.rs.<commit_after>/// Validates an over-the-air callsign. APRS-IS is more forgiving.
///
/// Valid callsigns are between 4 and 7 alphanumeric characters with at most a single hyphen `-`
/// separating the callsign and the SSID suffix. This function validates the callsign's length,
/// composition and SSID.
///
/// # Examples
///
/// ```rust
/// assert_eq!(valid_callsign("W2GMD"), true);
/// assert_eq!(valid_callsign("W2GMD-2"), true);
/// assert_eq!(valid_callsign("W2GMD*"), true);
/// ```
///
/// ```rust
/// assert_eq!(valid_callsign("W2GMD--1"), false);
/// assert_eq!(valid_callsign("W2GMD- 1"), false);
/// assert_eq!(valid_callsign("W2*MD-8"), false);
/// ```
///
pub fn valid_callsign(callsign: &str) -> bool {
    let mut callsign: &str = callsign.trim_matches(|x| x == '*' || (x as char).is_whitespace());
    let mut ssid: &str = "0";

    // Ensure single separator between callsign and SSID
    match callsign.matches('-').count() {
        0 => {},
        1 => {
            let call_components: Vec<&str> = callsign.split('-').collect();
            callsign = call_components[0];
            ssid = call_components[1];
        },
        _ => {return false;},
    }
    // Validate callsign
    match callsign.len() {
        4...6 => {},
        _ => {return false;},
    }
    for chr in callsign.chars() {
        if !chr.is_alphanumeric() {
            return false;
        }
    }
    // Validate SSID
    match ssid.len() {
        1...2 => {},
        _ => {return false;}
    }
    for chr in ssid.chars() {
        if !chr.is_numeric() {
            return false;
        }
    }
    match ssid.parse::<usize>()  {
        Err(e) => {return false;},
        Ok(s) => {
            match s {
                0...15 => {},
                _ => {return false;},
            }
        },
    }
    // Callsign and SSID have passed all checks, so return true
    true
}
<|endoftext|>


<commit_before>use rand::{self, Rng, thread_rng};
use rand::distributions::range::SampleRange;
use std::ops::Range;

// Flip a coin using the thread RNG.
pub fn coin_flip() -> bool {
    thread_rng().gen()
}

// Sample `amount` values from an iterable using the thread RNG.
pub fn sample<T, I>(iterable: I, amount: usize) -> Vec<T>
    where I: IntoIterator<Item=T>
{
    rand::sample(&mut thread_rng(), iterable, amount)
}

// Pick a random value from an iterable using the thread RNG.
pub fn pick<T, I>(iterable: I) -> T
    where I: IntoIterator<Item=T>
{
    sample(iterable, 1).remove(0)
}

// Sample a value from a range using the thread RNG.
pub fn random_range<T: PartialOrd + SampleRange>(r: Range<T>) -> T {
    thread_rng().gen_range(r.start, r.end)
}

// Sample two distinct values from a range using the thread RNG.
pub fn random_range_two<T: PartialOrd + SampleRange>(r: Range<T>) -> (T, T)
    where Range<T>: IntoIterator<Item = T>
{
    let mut s = sample(r, 2);
    let b = s.remove(1);
    let a = s.remove(0);
    (a, b)
}

pub fn punctuation_name(c: char) -> &'static str {
    match c {
        '!' => "exclamation mark",
        '"' => "quotation mark",
        '#' => "number sign",
        '$' => "dollar sign",
        '%' => "percent sign",
        '\'' => "apostrophe",
        '(' => "left parenthesis",
        ')' => "right parenthesis",
        '*' => "asterisk",
        '+' => "plus sign",
        ',' => "comma",
        '-' => "hyphen",
        '.' => "period",
        '/' => "slash",
        '0' => "zero",
        '1' => "one",
        '2' => "two",
        '3' => "three",
        '4' => "four",
        '5' => "five",
        '6' => "six",
        '7' => "seven",
        '8' => "eight",
        '9' => "nine",
        ':' => "colon",
        ';' => "semicolon",
        '<' => "less-than sign",
        '=' => "equals sign",
        '>' => "greater-than sign",
        '?' => "question mark",
        '@' => "at sign",
        _ => "strange character"
    }
}

pub fn ordinal(n: i32) -> String {
    let i = if 10 <= n && n <= 19 {0} else {n % 10};
    n.to_string() + ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"][i as usize]
}
<commit_msg>helper to shuffle randomly sampled values<commit_after>use rand::{self, Rng, thread_rng};
use rand::distributions::range::SampleRange;
use std::ops::Range;

// Flip a coin using the thread RNG.
pub fn coin_flip() -> bool {
    thread_rng().gen()
}

// Sample `amount` values from an iterable using the thread RNG.
// Order of sampled values will not be random.
pub fn sample<T, I>(iterable: I, amount: usize) -> Vec<T>
    where I: IntoIterator<Item=T>
{
    rand::sample(&mut thread_rng(), iterable, amount)
}

// Sample `amount` values from an iterable using the thread RNG.
// Order of sampled values will be random.
pub fn shuffle_sample<T, I>(iterable: I, amount: usize) -> Vec<T>
    where I: IntoIterator<Item=T>
{
    let mut sample_vec = sample(iterable, amount);
    thread_rng().shuffle(&mut sample_vec[..]);
    sample_vec
}

// Pick a random value from an iterable using the thread RNG.
pub fn pick<T, I>(iterable: I) -> T
    where I: IntoIterator<Item=T>
{
    sample(iterable, 1).remove(0)
}

// Sample a value from a range using the thread RNG.
pub fn random_range<T: PartialOrd + SampleRange>(r: Range<T>) -> T {
    thread_rng().gen_range(r.start, r.end)
}

// Sample two distinct values from a range using the thread RNG.
pub fn random_range_two<T: PartialOrd + SampleRange>(r: Range<T>) -> (T, T)
    where Range<T>: IntoIterator<Item = T>
{
    let mut s = shuffle_sample(r, 2);
    let b = s.remove(1);
    let a = s.remove(0);
    (a, b)
}

pub fn punctuation_name(c: char) -> &'static str {
    match c {
        '!' => "exclamation mark",
        '"' => "quotation mark",
        '#' => "number sign",
        '$' => "dollar sign",
        '%' => "percent sign",
        '\'' => "apostrophe",
        '(' => "left parenthesis",
        ')' => "right parenthesis",
        '*' => "asterisk",
        '+' => "plus sign",
        ',' => "comma",
        '-' => "hyphen",
        '.' => "period",
        '/' => "slash",
        '0' => "zero",
        '1' => "one",
        '2' => "two",
        '3' => "three",
        '4' => "four",
        '5' => "five",
        '6' => "six",
        '7' => "seven",
        '8' => "eight",
        '9' => "nine",
        ':' => "colon",
        ';' => "semicolon",
        '<' => "less-than sign",
        '=' => "equals sign",
        '>' => "greater-than sign",
        '?' => "question mark",
        '@' => "at sign",
        _ => "strange character"
    }
}

pub fn ordinal(n: i32) -> String {
    let i = if 10 <= n && n <= 19 {0} else {n % 10};
    n.to_string() + ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"][i as usize]
}
<|endoftext|>