<commit_before>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;
use super::*;
use super::wrap;

use super::super::cli::SocketLend;

use std::collections::{BTreeMap, BTreeSet};
use std::collections::btree_map::Entry;

const RATIO_TO_COCATEGORIZE: f64 = 0.8;
const COCATEGORY_TRAVEL_DISTANCE: i32 = 4;
const COCATEGORIZE_MAGNITUDE: i32 = 32;

impl<R: rand::Rng> Lexicon<R> {
    /// Make a new lexion. It needs its own Rng for internal purposes of learning.
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            cocategorization_ratio: RATIO_TO_COCATEGORIZE,
            cocategory_travel_distance: COCATEGORY_TRAVEL_DISTANCE,
            cocategorize_magnitude: COCATEGORIZE_MAGNITUDE,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    /// Get a source by its unique name.
    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    /// Get an author identifier from a particular source.
    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    /// Tell a message to the lexicon and potentially get a response back.
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) {
        let conversation = match self.active_conversations.entry(source.clone()) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // Learn the message immediately
        self.learn(message);
    }

    /// Switch conversations
    pub fn switch(&mut self, source: SourceCell) {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(source.clone()) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());
    }

    /// Say something based on the conversation context
    pub fn respond(&mut self, source: SourceCell) -> Option<(String, String)> {
        use std::collections::VecDeque;
        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        // Make a double-ended vec for building the message out of categories
        let mut instances = VecDeque::new();

        // Push the base instance onto the categories vec
        instances.push_back(self.rng.choose(&base.borrow().instances[..]).unwrap().clone());

        let forward_instance_chooser = |b: &Category, rng: &mut R| {
            // Find the count of how many word instances exist total
            let mut count = b.instances.len();
            for cocategory in &b.precocategories {
                count += cocategory.borrow().instances.len();
            }
            // Generate an index based on the count
            let mut i = rng.gen_range(0, count);
            match b.instances.get(i) {
                // It was in the original category
                Some(ins) => ins.clone(),
                // It was in a cocategory
                None => {
                    // Subtract the cocategory length from the index
                    i -= b.instances.len();
                    for cocategory in &b.precocategories {
                        let b = cocategory.borrow();
                        // If it was in this category
                        if let Some(ins) = b.instances.get(i) {
                            // Clone the instance and return it
                            return ins.clone();
                        }
                        // Otherwise subtract by the amount of instances in this cocategory
                        i -= b.instances.len();
                    }
                    // The index should point to some category, so this is unreachable
                    unreachable!();
                },
            }
        };

        let backward_instance_chooser = |b: &Category, rng: &mut R| {
            // Find the count of how many word instances exist total
            let mut count = b.instances.len();
            for cocategory in &b.postcocategories {
                count += cocategory.borrow().instances.len();
            }
            // Generate an index based on the count
            let mut i = rng.gen_range(0, count);
            match b.instances.get(i) {
                // It was in the original category
                Some(ins) => ins.clone(),
                // It was in a cocategory
                None => {
                    // Subtract the cocategory length from the index
                    i -= b.instances.len();
                    for cocategory in &b.postcocategories {
                        let b = cocategory.borrow();
                        // If it was in this category
                        if let Some(ins) = b.instances.get(i) {
                            // Clone the instance and return it
                            return ins.clone();
                        }
                        // Otherwise subtract by the amount of instances in this cocategory
                        i -= b.instances.len();
                    }
                    // The index should point to some category, so this is unreachable
                    unreachable!();
                },
            }
        };

        // Iterate forwards weaving between messages and adding instances to the vec
        loop {
            let ins = instances.back().unwrap().borrow().next_instance();
            if let Some(i) = ins {
                let b = i.borrow();
                instances.push_back(forward_instance_chooser(&b.category.borrow(), &mut self.rng));
            } else {
                break;
            }
        }
        // Iterate backwards to reach the beginning of the message
        loop {
            let ins = instances.front().unwrap().borrow().prev_instance();
            if let Some(i) = ins {
                let b = i.borrow();
                instances.push_front(backward_instance_chooser(&b.category.borrow(), &mut self.rng));
            } else {
                break;
            }
        }

        Some((
            instances.iter()
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            instances.iter()
                .cloned()
                .map(|mut instance| {
                    for _ in 0..self.cocategory_travel_distance {
                        instance = {
                            let b = instance.borrow();
                            if self.rng.gen_range(0, 2) == 0 {
                                backward_instance_chooser(&b.category.borrow(), &mut self.rng)
                            } else {
                                forward_instance_chooser(&b.category.borrow(), &mut self.rng)
                            }
                        };
                    }
                    instance
                })
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" ")
        ))
    }

    /// Have seifmios attempt to initiate a conversation at a source, but it may fail.
    pub fn initiate(&mut self, source: SourceCell) -> Option<(String, String)> {
        self.switch(source.clone());
        self.respond(source)
    }

    /// Thinks one iteration
    pub fn think(&mut self) {
        // Learn a random message if there are some
        let m = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return,
        };

        self.learn(m.clone());

        for _ in 0..self.cocategorize_magnitude {
            // Get two random categories (we already know messages exist from above)
            Category::cocategorize((
                {
                    let b = m.borrow();
                    let b = self.rng.choose(&b.instances[..]).unwrap().borrow();
                    b.category.clone()
                },
                {
                    let b = self.rng.choose(&self.messages[..]).unwrap().borrow();
                    let b = self.rng.choose(&b.instances[..]).unwrap().borrow();
                    b.category.clone()
                },
            ), self.cocategorization_ratio);
        }
    }

    pub fn learn(&mut self, message: MessageCell) {
        // Vector of absolute perfect matches
        let mut vones = Vec::new();

        // Look through each word in the message
        for word in &message.borrow().instances {
            let borrow = word.borrow();
            // Check each instance in that words instances
            for instance in &borrow.word.borrow().instances {
                // Get the message for each instance
                let omessage = instance.borrow().message.clone();
                // Find what kind of matches exist between the messages
                if let Mismatch::One(best) =
                    Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                    vones.push(best);
                }
            }
        }

        // Now that we have perfect matches, merge them into the same Category
        for ms in vones {
            // We only want to combine if they aren't already in the same category
            if ms.0.borrow().category != ms.1.borrow().category {
                let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                Category::merge(cats);
            }
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self, socket: &mut SocketLend) {
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(ib.category.clone());
            }
        }

        let len = set.len();

        for cat in set {
            let catr = cat.borrow();
            if catr.instances.len() != 1 {
                socket.msg("Category:");
                for cocategory in &catr.precocategories {
                    socket.msg("\tPre-Cocategory:");
                    let catr = cocategory.borrow();
                    for instance in &catr.instances {
                        let ib = instance.borrow();
                        socket.msg(&format!("\t\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                    }
                }
                for cocategory in &catr.postcocategories {
                    socket.msg("\tPost-Cocategory:");
                    let catr = cocategory.borrow();
                    for instance in &catr.instances {
                        let ib = instance.borrow();
                        socket.msg(&format!("\t\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                    }
                }
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                }
            }
        }
    }

    pub fn find_relation(&self, words: (String, String), socket: &mut SocketLend) {
        let wls = (self.words.get(&words.0), self.words.get(&words.1));
        match wls {
            (Some(w0), Some(w1)) => {
                let b = w0.borrow();
                for instance in &b.instances {
                    let b = instance.borrow();
                    let cb = b.category.borrow();

                    for cins in &cb.instances {
                        if cins.borrow().word == *w1 {
                            socket.msg("Category:");
                            for instance in &cb.instances {
                                let ib = instance.borrow();
                                socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                            }
                            break;
                        }
                    }

                    // Check all the precategories
                    for prec in &cb.precocategories {
                        let cb = prec.borrow();
                        for cins in &cb.instances {
                            if cins.borrow().word == *w1 {
                                socket.msg("Pre-Cocategory:");
                                for instance in &cb.instances {
                                    let ib = instance.borrow();
                                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                                }
                                break;
                            }
                        }
                    }

                    // Check all the postcategories
                    for postc in &cb.postcocategories {
                        let cb = postc.borrow();
                        for cins in &cb.instances {
                            if cins.borrow().word == *w1 {
                                socket.msg("Post-Cocategory:");
                                for instance in &cb.instances {
                                    let ib = instance.borrow();
                                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                                }
                                break;
                            }
                        }
                    }
                }
            },
            (Some(_), None) => {
                socket.msg(&format!("Ignored: Word \"{}\" coldn't be found", words.0));
            },
            (None, Some(_)) => {
                socket.msg(&format!("Ignored: Word \"{}\" coldn't be found", words.1));
            },
            (None, None) => {
                socket.msg("Ignored: Neither word could be found");
            },
        }
    }
}
<commit_msg>make seifmios respond somewhat properly<commit_after>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;
use super::*;
use super::wrap;

use super::super::cli::SocketLend;

use std::collections::{BTreeMap, BTreeSet};
use std::collections::btree_map::Entry;

const RATIO_TO_COCATEGORIZE: f64 = 0.8;
const COCATEGORY_TRAVEL_DISTANCE: i32 = 4;
const COCATEGORIZE_MAGNITUDE: i32 = 32;

impl<R: rand::Rng> Lexicon<R> {
    /// Make a new lexion. It needs its own Rng for internal purposes of learning.
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            cocategorization_ratio: RATIO_TO_COCATEGORIZE,
            cocategory_travel_distance: COCATEGORY_TRAVEL_DISTANCE,
            cocategorize_magnitude: COCATEGORIZE_MAGNITUDE,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    /// Get a source by its unique name.
    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    /// Get an author identifier from a particular source.
    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    /// Tell a message to the lexicon and potentially get a response back.
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) {
        let conversation = match self.active_conversations.entry(source.clone()) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // Learn the message immediately
        self.learn(message);
    }

    /// Switch conversations
    pub fn switch(&mut self, source: SourceCell) {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(source.clone()) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());
    }

    /// Say something based on the conversation context
    pub fn respond(&mut self, source: SourceCell) -> Option<(String, String)> {
        use std::collections::VecDeque;
        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        // Make a double-ended vec for building the message out of categories
        let mut instances = VecDeque::new();

        let mut orig_index = if let Some(con) = self.active_conversations.get(&source) {
            let cb = con.borrow();
            if let Some(conm) = cb.messages.last() {
                let mb = (conm).borrow();
                instances.push_back(self.rng.choose(&mb.instances[..]).unwrap().clone());
                0
            } else {
                instances.push_back(self.rng.choose(&base.borrow().instances[..]).unwrap().clone());
                8192
            }
        } else {
            instances.push_back(self.rng.choose(&base.borrow().instances[..]).unwrap().clone());
            8192
        };

        let forward_instance_chooser = |b: &Category, rng: &mut R| {
            // Find the count of how many word instances exist total
            let mut count = b.instances.len();
            for cocategory in &b.precocategories {
                count += cocategory.borrow().instances.len();
            }
            // Generate an index based on the count
            let mut i = rng.gen_range(0, count);
            match b.instances.get(i) {
                // It was in the original category
                Some(ins) => ins.clone(),
                // It was in a cocategory
                None => {
                    // Subtract the cocategory length from the index
                    i -= b.instances.len();
                    for cocategory in &b.precocategories {
                        let b = cocategory.borrow();
                        // If it was in this category
                        if let Some(ins) = b.instances.get(i) {
                            // Clone the instance and return it
                            return ins.clone();
                        }
                        // Otherwise subtract by the amount of instances in this cocategory
                        i -= b.instances.len();
                    }
                    // The index should point to some category, so this is unreachable
                    unreachable!();
                },
            }
        };

        let backward_instance_chooser = |b: &Category, rng: &mut R| {
            // Find the count of how many word instances exist total
            let mut count = b.instances.len();
            for cocategory in &b.postcocategories {
                count += cocategory.borrow().instances.len();
            }
            // Generate an index based on the count
            let mut i = rng.gen_range(0, count);
            match b.instances.get(i) {
                // It was in the original category
                Some(ins) => ins.clone(),
                // It was in a cocategory
                None => {
                    // Subtract the cocategory length from the index
                    i -= b.instances.len();
                    for cocategory in &b.postcocategories {
                        let b = cocategory.borrow();
                        // If it was in this category
                        if let Some(ins) = b.instances.get(i) {
                            // Clone the instance and return it
                            return ins.clone();
                        }
                        // Otherwise subtract by the amount of instances in this cocategory
                        i -= b.instances.len();
                    }
                    // The index should point to some category, so this is unreachable
                    unreachable!();
                },
            }
        };

        // Iterate forwards weaving between messages and adding instances to the vec
        loop {
            let ins = instances.back().unwrap().borrow().next_instance();
            if let Some(i) = ins {
                let b = i.borrow();
                instances.push_back(forward_instance_chooser(&b.category.borrow(), &mut self.rng));
            } else {
                break;
            }
        }
        // Iterate backwards to reach the beginning of the message
        loop {
            let ins = instances.front().unwrap().borrow().prev_instance();
            if let Some(i) = ins {
                let b = i.borrow();
                instances.push_front(backward_instance_chooser(&b.category.borrow(), &mut self.rng));
                orig_index += 1;
            } else {
                break;
            }
        }

        Some((
            instances.iter()
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            instances.iter()
                .cloned()
                .enumerate()
                .map(|(index, mut instance)| {
                    if index != orig_index {
                        for _ in 0..self.cocategory_travel_distance {
                            instance = {
                                let b = instance.borrow();
                                if self.rng.gen_range(0, 2) == 0 {
                                    backward_instance_chooser(&b.category.borrow(), &mut self.rng)
                                } else {
                                    forward_instance_chooser(&b.category.borrow(), &mut self.rng)
                                }
                            };
                        }
                    }
                    instance
                })
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" ")
        ))
    }

    /// Have seifmios attempt to initiate a conversation at a source, but it may fail.
    pub fn initiate(&mut self, source: SourceCell) -> Option<(String, String)> {
        self.switch(source.clone());
        self.respond(source)
    }

    /// Thinks one iteration
    pub fn think(&mut self) {
        // Learn a random message if there are some
        let m = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return,
        };

        self.learn(m.clone());

        for _ in 0..self.cocategorize_magnitude {
            // Get two random categories (we already know messages exist from above)
            Category::cocategorize((
                {
                    let b = m.borrow();
                    let b = self.rng.choose(&b.instances[..]).unwrap().borrow();
                    b.category.clone()
                },
                {
                    let b = self.rng.choose(&self.messages[..]).unwrap().borrow();
                    let b = self.rng.choose(&b.instances[..]).unwrap().borrow();
                    b.category.clone()
                },
            ), self.cocategorization_ratio);
        }
    }

    pub fn learn(&mut self, message: MessageCell) {
        // Vector of absolute perfect matches
        let mut vones = Vec::new();

        // Look through each word in the message
        for word in &message.borrow().instances {
            let borrow = word.borrow();
            // Check each instance in that words instances
            for instance in &borrow.word.borrow().instances {
                // Get the message for each instance
                let omessage = instance.borrow().message.clone();
                // Find what kind of matches exist between the messages
                if let Mismatch::One(best) =
                    Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                    vones.push(best);
                }
            }
        }

        // Now that we have perfect matches, merge them into the same Category
        for ms in vones {
            // We only want to combine if they aren't already in the same category
            if ms.0.borrow().category != ms.1.borrow().category {
                let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                Category::merge(cats);
            }
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self, socket: &mut SocketLend) {
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(ib.category.clone());
            }
        }

        for cat in set {
            let catr = cat.borrow();
            if catr.instances.len() != 1 {
                socket.msg("Category:");
                for cocategory in &catr.precocategories {
                    socket.msg("\tPre-Cocategory:");
                    let catr = cocategory.borrow();
                    for instance in &catr.instances {
                        let ib = instance.borrow();
                        socket.msg(&format!("\t\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                    }
                }
                for cocategory in &catr.postcocategories {
                    socket.msg("\tPost-Cocategory:");
                    let catr = cocategory.borrow();
                    for instance in &catr.instances {
                        let ib = instance.borrow();
                        socket.msg(&format!("\t\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                    }
                }
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                }
            }
        }
    }

    pub fn find_relation(&self, words: (String, String), socket: &mut SocketLend) {
        let wls = (self.words.get(&words.0), self.words.get(&words.1));
        match wls {
            (Some(w0), Some(w1)) => {
                let b = w0.borrow();
                for instance in &b.instances {
                    let b = instance.borrow();
                    let cb = b.category.borrow();

                    for cins in &cb.instances {
                        if cins.borrow().word == *w1 {
                            socket.msg("Category:");
                            for instance in &cb.instances {
                                let ib = instance.borrow();
                                socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                            }
                            break;
                        }
                    }

                    // Check all the precategories
                    for prec in &cb.precocategories {
                        let cb = prec.borrow();
                        for cins in &cb.instances {
                            if cins.borrow().word == *w1 {
                                socket.msg("Pre-Cocategory:");
                                for instance in &cb.instances {
                                    let ib = instance.borrow();
                                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                                }
                                break;
                            }
                        }
                    }

                    // Check all the postcategories
                    for postc in &cb.postcocategories {
                        let cb = postc.borrow();
                        for cins in &cb.instances {
                            if cins.borrow().word == *w1 {
                                socket.msg("Post-Cocategory:");
                                for instance in &cb.instances {
                                    let ib = instance.borrow();
                                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                                }
                                break;
                            }
                        }
                    }
                }
            },
            (Some(_), None) => {
                socket.msg(&format!("Ignored: Word \"{}\" coldn't be found", words.0));
            },
            (None, Some(_)) => {
                socket.msg(&format!("Ignored: Word \"{}\" coldn't be found", words.1));
            },
            (None, None) => {
                socket.msg("Ignored: Neither word could be found");
            },
        }
    }
}
<|endoftext|>


<commit_before>// $ cargo bench --features full --bench rust

#![recursion_limit = "256"]
#![feature(rustc_private)]

extern crate rustc_data_structures;
extern crate syntax;
extern crate syntax_pos;

#[macro_use]
#[path = "../tests/macros/mod.rs"]
mod macros;

#[path = "../tests/common/mod.rs"]
mod common;

use proc_macro2::TokenStream;
use rustc_data_structures::sync::Lrc;
use std::fs;
use std::str::FromStr;
use std::time::{Duration, Instant};
use syntax::edition::Edition;
use syntax::errors::{emitter::Emitter, DiagnosticBuilder, Handler};
use syntax::parse::ParseSess;
use syntax::source_map::{FilePathMapping, SourceMap};
use syntax_pos::FileName;

fn tokenstream_parse(content: &str) -> Result<(), ()> {
    TokenStream::from_str(content).map(drop).map_err(drop)
}

fn syn_parse(content: &str) -> Result<(), ()> {
    syn::parse_file(content).map(drop).map_err(drop)
}

fn libsyntax_parse(content: &str) -> Result<(), ()> {
    struct SilentEmitter;

    impl Emitter for SilentEmitter {
        fn emit_diagnostic(&mut self, _db: &DiagnosticBuilder) {}
    }

    syntax::with_globals(Edition::Edition2018, || {
        let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
        let emitter = Box::new(SilentEmitter);
        let handler = Handler::with_emitter(false, None, emitter);
        let sess = ParseSess::with_span_handler(handler, cm);
        if let Err(mut diagnostic) = syntax::parse::parse_crate_from_source_str(
            FileName::Custom("bench".to_owned()),
            content.to_owned(),
            &sess,
        ) {
            diagnostic.cancel();
            return Err(());
        };
        Ok(())
    })
}

fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration {
    let begin = Instant::now();
    let mut success = 0;
    let mut total = 0;

    walkdir::WalkDir::new("tests/rust/src")
        .into_iter()
        .filter_entry(common::base_dir_filter)
        .for_each(|entry| {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_dir() {
                return;
            }
            let content = fs::read_to_string(path).unwrap();
            let ok = codepath(&content).is_ok();
            success += ok as usize;
            total += 1;
            if !ok {
                eprintln!("FAIL {}", path.display());
            }
        });

    assert_eq!(success, total);
    begin.elapsed()
}

fn main() {
    common::clone_rust();

    macro_rules! testcases {
        ($($name:ident),*) => {
            vec![
                $(
                    (stringify!($name), $name as fn(&str) -> Result<(), ()>),
                )*
            ]
        };
    }

    let mut lines = 0;
    exec(|content| {
        lines += content.lines().count();
        Ok(())
    });
    eprintln!("\n{} lines", lines);

    for (name, f) in testcases!(tokenstream_parse, syn_parse, libsyntax_parse) {
        eprint!("{:20}", format!("{}:", name));
        let elapsed = exec(f);
        eprintln!(
            "elapsed={}.{:03}s",
            elapsed.as_secs(),
            elapsed.subsec_millis(),
        );
    }
    eprintln!();
}
<commit_msg>Add baseline benchmark that traverses repo without parsing<commit_after>// $ cargo bench --features full --bench rust

#![recursion_limit = "256"]
#![feature(rustc_private)]

extern crate rustc_data_structures;
extern crate syntax;
extern crate syntax_pos;

#[macro_use]
#[path = "../tests/macros/mod.rs"]
mod macros;

#[path = "../tests/common/mod.rs"]
mod common;

use proc_macro2::TokenStream;
use rustc_data_structures::sync::Lrc;
use std::fs;
use std::str::FromStr;
use std::time::{Duration, Instant};
use syntax::edition::Edition;
use syntax::errors::{emitter::Emitter, DiagnosticBuilder, Handler};
use syntax::parse::ParseSess;
use syntax::source_map::{FilePathMapping, SourceMap};
use syntax_pos::FileName;

fn tokenstream_parse(content: &str) -> Result<(), ()> {
    TokenStream::from_str(content).map(drop).map_err(drop)
}

fn syn_parse(content: &str) -> Result<(), ()> {
    syn::parse_file(content).map(drop).map_err(drop)
}

fn libsyntax_parse(content: &str) -> Result<(), ()> {
    struct SilentEmitter;

    impl Emitter for SilentEmitter {
        fn emit_diagnostic(&mut self, _db: &DiagnosticBuilder) {}
    }

    syntax::with_globals(Edition::Edition2018, || {
        let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
        let emitter = Box::new(SilentEmitter);
        let handler = Handler::with_emitter(false, None, emitter);
        let sess = ParseSess::with_span_handler(handler, cm);
        if let Err(mut diagnostic) = syntax::parse::parse_crate_from_source_str(
            FileName::Custom("bench".to_owned()),
            content.to_owned(),
            &sess,
        ) {
            diagnostic.cancel();
            return Err(());
        };
        Ok(())
    })
}

fn read_from_disk(content: &str) -> Result<(), ()> {
    let _ = content;
    Ok(())
}

fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration {
    let begin = Instant::now();
    let mut success = 0;
    let mut total = 0;

    walkdir::WalkDir::new("tests/rust/src")
        .into_iter()
        .filter_entry(common::base_dir_filter)
        .for_each(|entry| {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_dir() {
                return;
            }
            let content = fs::read_to_string(path).unwrap();
            let ok = codepath(&content).is_ok();
            success += ok as usize;
            total += 1;
            if !ok {
                eprintln!("FAIL {}", path.display());
            }
        });

    assert_eq!(success, total);
    begin.elapsed()
}

fn main() {
    common::clone_rust();

    macro_rules! testcases {
        ($($name:ident,)*) => {
            vec![
                $(
                    (stringify!($name), $name as fn(&str) -> Result<(), ()>),
                )*
            ]
        };
    }

    let mut lines = 0;
    exec(|content| {
        lines += content.lines().count();
        Ok(())
    });
    eprintln!("\n{} lines", lines);

    for (name, f) in testcases!(
        read_from_disk,
        tokenstream_parse,
        syn_parse,
        libsyntax_parse,
    ) {
        eprint!("{:20}", format!("{}:", name));
        let elapsed = exec(f);
        eprintln!(
            "elapsed={}.{:03}s",
            elapsed.as_secs(),
            elapsed.subsec_millis(),
        );
    }
    eprintln!();
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::code_block::CodeBlock;
use crate::errors::{CompilationError, Error};
use crate::EvalContext;
use json;
use regex::Regex;
use std;
use std::fs;
use std::path::{Path, PathBuf};
use which;

fn shared_object_name_from_crate_name(crate_name: &str) -> String {
    if cfg!(target_os = "macos") {
        format!("lib{}.dylib", crate_name)
    } else if cfg!(target_os = "windows") {
        format!("{}.dll", crate_name)
    } else {
        format!("lib{}.so", crate_name)
    }
}

fn create_dir(dir: &Path) -> Result<(), Error> {
    if let Err(err) = fs::create_dir_all(dir) {
        bail!("Error creating directory '{:?}': {}", dir, err);
    }
    Ok(())
}

fn write_file(dir: &Path, basename: &str, contents: &str) -> Result<(), Error> {
    create_dir(dir)?;
    let filename = dir.join(basename);
    if let Err(err) = fs::write(&filename, contents) {
        bail!("Error writing '{:?}': {}", filename, err);
    }
    Ok(())
}

/// On Mac, if we copy the dylib, we get intermittent failures where we end up
/// with the previous version of the file when we shouldn't. On windows, if
/// rename the file, we get errors subsequently when something (perhaps the
/// Windows linker) tries to delete the file that it expects to still be there.
/// On Linux either renaming or copying works, but renaming should be more
/// efficient, so we do that.
#[cfg(windows)]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    // Copy file by reading and writing instead of using std::fs::copy. The src
    // is a hard-linked file and we want to make extra sure that we end up with
    // a completely indepent copy.
    fn alt_copy(src: &Path, dest: &Path) -> Result<(), std::io::Error> {
        use std::fs::File;
        std::io::copy(&mut File::open(src)?, &mut File::create(dest)?)?;
        Ok(())
    }
    if let Err(err) = alt_copy(src, dest) {
        bail!("Error copying '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

#[cfg(not(windows))]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    if let Err(err) = fs::rename(src, dest) {
        bail!("Error renaming '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

pub(crate) struct Module {
    pub(crate) tmpdir: PathBuf,
    build_num: i32,
    /// Whether to pass -Ztime-passes to the compiler and print the result.
    /// Causes the nightly compiler, which must be installed to be selected.
    pub(crate) time_passes: bool,
    pub(crate) linker: String,
    sccache: Option<PathBuf>,
}

const CRATE_NAME: &str = "ctx";

impl Module {
    pub(crate) fn new(tmpdir: PathBuf) -> Result<Module, Error> {
        let linker = if which::which("lld").is_ok() {
            "lld".to_owned()
        } else {
            "system".to_owned()
        };
        let module = Module {
            tmpdir,
            build_num: 0,
            time_passes: false,
            linker,
            sccache: None,
        };
        Ok(module)
    }

    pub(crate) fn deps_dir(&self) -> PathBuf {
        self.target_dir().join("debug").join("deps")
    }

    fn target_dir(&self) -> PathBuf {
        self.tmpdir.join("target")
    }

    fn so_path(&self) -> PathBuf {
        self.deps_dir()
            .join(shared_object_name_from_crate_name(CRATE_NAME))
    }

    fn src_dir(&self) -> PathBuf {
        self.tmpdir.join("src")
    }

    pub(crate) fn crate_dir(&self) -> &Path {
        &self.tmpdir
    }

    pub fn set_sccache(&mut self, enabled: bool) -> Result<(), Error> {
        if enabled {
            if let Ok(path) = which::which("sccache") {
                self.sccache = Some(path);
            } else {
                bail!("Couldn't find sccache. Try running `cargo install sscache`.");
            }
        } else {
            self.sccache = None;
        }
        Ok(())
    }

    pub fn sccache(&self) -> bool {
        self.sccache.is_some()
    }

    // Writes Cargo.toml. Should be called before compile.
    pub(crate) fn write_cargo_toml(&self, eval_context: &EvalContext) -> Result<(), Error> {
        write_file(
            self.crate_dir(),
            "Cargo.toml",
            &self.get_cargo_toml_contents(eval_context),
        )
    }

    pub(crate) fn compile(&mut self, code_block: &CodeBlock) -> Result<SoFile, Error> {
        write_file(&self.src_dir(), "lib.rs", &code_block.to_string())?;

        // Our compiler errors should all be in JSON format, but for errors from Cargo errors, we
        // need to add explicit matching for those errors that we expect we might see.
        lazy_static! {
            static ref KNOWN_NON_JSON_ERRORS: Regex =
                Regex::new("(error: no matching package named)").unwrap();
        }

        let mut command = std::process::Command::new("cargo");
        if self.time_passes {
            command.arg("+nightly");
        }
        command
            .arg("rustc")
            .arg("--message-format=json")
            .arg("--")
            .arg("-C")
            .arg("prefer-dynamic")
            .env("CARGO_TARGET_DIR", "target")
            .current_dir(self.crate_dir());
        if self.linker != "system" {
            command
                .arg("-C")
                .arg(format!("link-arg=-fuse-ld={}", self.linker));
        }
        if let Some(sccache) = &self.sccache {
            command.env("RUSTC_WRAPPER", sccache);
        }
        if self.time_passes {
            command.arg("-Ztime-passes");
        }
        let cargo_output = command.output()?;
        if cargo_output.status.success() {
            if self.time_passes {
                let stdout = String::from_utf8_lossy(&cargo_output.stdout);
                eprintln!("{}", stdout);
            }
        } else {
            let stderr = String::from_utf8_lossy(&cargo_output.stderr);
            let stdout = String::from_utf8_lossy(&cargo_output.stdout);
            let mut non_json_error = None;
            let errors: Vec<CompilationError> = stderr
                .lines()
                .chain(stdout.lines())
                .filter_map(|line| {
                    json::parse(&line)
                        .ok()
                        .and_then(|json| CompilationError::opt_new(json, code_block))
                        .or_else(|| {
                            if KNOWN_NON_JSON_ERRORS.is_match(line) {
                                non_json_error = Some(line);
                            }
                            None
                        })
                })
                .collect();
            if errors.is_empty() {
                if let Some(error) = non_json_error {
                    bail!(Error::Message(error.to_owned()));
                } else {
                    bail!(Error::Message(format!(
                        "Compilation failed, but no parsable errors were found. STDERR:\n\
                         {}\nSTDOUT:{}\n",
                        stderr, stdout
                    )));
                }
            } else {
                bail!(Error::CompilationErrors(errors));
            }
        }
        self.build_num += 1;
        let copied_so_file = self
            .deps_dir()
            .join(shared_object_name_from_crate_name(&format!(
                "code_{}",
                self.build_num
            )));
        // Every time we compile, the output file is the same. We need to
        // renamed it so that we have a unique filename, otherwise we wouldn't
        // be able to load the result of the next compilation. Also, on Windows,
        // a loaded dll gets locked, so we couldn't even compile a second time
        // if we didn't load a different file.
        rename_or_copy_so_file(&self.so_path(), &copied_so_file)?;
        Ok(SoFile {
            path: copied_so_file,
        })
    }

    fn get_cargo_toml_contents(&self, eval_context: &EvalContext) -> String {
        let crate_imports = eval_context.format_cargo_deps();
        format!(
            r#"
[package]
name = "{}"
version = "1.0.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[profile.dev]
opt-level = {}
debug = false
rpath = true
lto = false
debug-assertions = true
codegen-units = 16
panic = 'unwind'
incremental = true
overflow-checks = true

[dependencies]
{}
"#,
            CRATE_NAME,
            eval_context.opt_level(),
            crate_imports
        )
    }
}

pub(crate) struct SoFile {
    pub(crate) path: PathBuf,
}
<commit_msg>Check that compilation with lld as the linker actually works before committing to using it.<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::code_block::CodeBlock;
use crate::errors::{CompilationError, Error};
use crate::EvalContext;
use json;
use regex::Regex;
use std;
use std::fs;
use std::path::{Path, PathBuf};
use which;

fn shared_object_name_from_crate_name(crate_name: &str) -> String {
    if cfg!(target_os = "macos") {
        format!("lib{}.dylib", crate_name)
    } else if cfg!(target_os = "windows") {
        format!("{}.dll", crate_name)
    } else {
        format!("lib{}.so", crate_name)
    }
}

fn create_dir(dir: &Path) -> Result<(), Error> {
    if let Err(err) = fs::create_dir_all(dir) {
        bail!("Error creating directory '{:?}': {}", dir, err);
    }
    Ok(())
}

fn write_file(dir: &Path, basename: &str, contents: &str) -> Result<(), Error> {
    create_dir(dir)?;
    let filename = dir.join(basename);
    if let Err(err) = fs::write(&filename, contents) {
        bail!("Error writing '{:?}': {}", filename, err);
    }
    Ok(())
}

/// On Mac, if we copy the dylib, we get intermittent failures where we end up
/// with the previous version of the file when we shouldn't. On windows, if
/// rename the file, we get errors subsequently when something (perhaps the
/// Windows linker) tries to delete the file that it expects to still be there.
/// On Linux either renaming or copying works, but renaming should be more
/// efficient, so we do that.
#[cfg(windows)]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    // Copy file by reading and writing instead of using std::fs::copy. The src
    // is a hard-linked file and we want to make extra sure that we end up with
    // a completely indepent copy.
    fn alt_copy(src: &Path, dest: &Path) -> Result<(), std::io::Error> {
        use std::fs::File;
        std::io::copy(&mut File::open(src)?, &mut File::create(dest)?)?;
        Ok(())
    }
    if let Err(err) = alt_copy(src, dest) {
        bail!("Error copying '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

#[cfg(not(windows))]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    if let Err(err) = fs::rename(src, dest) {
        bail!("Error renaming '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

pub(crate) struct Module {
    pub(crate) tmpdir: PathBuf,
    build_num: i32,
    /// Whether to pass -Ztime-passes to the compiler and print the result.
    /// Causes the nightly compiler, which must be installed to be selected.
    pub(crate) time_passes: bool,
    pub(crate) linker: String,
    sccache: Option<PathBuf>,
}

const CRATE_NAME: &str = "ctx";

impl Module {
    pub(crate) fn new(tmpdir: PathBuf) -> Result<Module, Error> {
        let linker = if which::which("lld").is_ok() {
            "lld".to_owned()
        } else {
            "system".to_owned()
        };
        let mut module = Module {
            tmpdir,
            build_num: 0,
            time_passes: false,
            linker,
            sccache: None,
        };
        // If we found a binary called lld on the path, make sure we can
        // actually compile code and use it to link. If not, fall back to the
        // system linker.
        if module.linker == "lld"
            && module
                .compile(&CodeBlock::new().generated("pub fn user_code_0() {}"))
                .is_err()
        {
            module.linker = "system".to_owned();
        }
        Ok(module)
    }

    pub(crate) fn deps_dir(&self) -> PathBuf {
        self.target_dir().join("debug").join("deps")
    }

    fn target_dir(&self) -> PathBuf {
        self.tmpdir.join("target")
    }

    fn so_path(&self) -> PathBuf {
        self.deps_dir()
            .join(shared_object_name_from_crate_name(CRATE_NAME))
    }

    fn src_dir(&self) -> PathBuf {
        self.tmpdir.join("src")
    }

    pub(crate) fn crate_dir(&self) -> &Path {
        &self.tmpdir
    }

    pub fn set_sccache(&mut self, enabled: bool) -> Result<(), Error> {
        if enabled {
            if let Ok(path) = which::which("sccache") {
                self.sccache = Some(path);
            } else {
                bail!("Couldn't find sccache. Try running `cargo install sscache`.");
            }
        } else {
            self.sccache = None;
        }
        Ok(())
    }

    pub fn sccache(&self) -> bool {
        self.sccache.is_some()
    }

    // Writes Cargo.toml. Should be called before compile.
    pub(crate) fn write_cargo_toml(&self, eval_context: &EvalContext) -> Result<(), Error> {
        write_file(
            self.crate_dir(),
            "Cargo.toml",
            &self.get_cargo_toml_contents(eval_context),
        )
    }

    pub(crate) fn compile(&mut self, code_block: &CodeBlock) -> Result<SoFile, Error> {
        write_file(&self.src_dir(), "lib.rs", &code_block.to_string())?;

        // Our compiler errors should all be in JSON format, but for errors from Cargo errors, we
        // need to add explicit matching for those errors that we expect we might see.
        lazy_static! {
            static ref KNOWN_NON_JSON_ERRORS: Regex =
                Regex::new("(error: no matching package named)").unwrap();
        }

        let mut command = std::process::Command::new("cargo");
        if self.time_passes {
            command.arg("+nightly");
        }
        command
            .arg("rustc")
            .arg("--message-format=json")
            .arg("--")
            .arg("-C")
            .arg("prefer-dynamic")
            .env("CARGO_TARGET_DIR", "target")
            .current_dir(self.crate_dir());
        if self.linker != "system" {
            command
                .arg("-C")
                .arg(format!("link-arg=-fuse-ld={}", self.linker));
        }
        if let Some(sccache) = &self.sccache {
            command.env("RUSTC_WRAPPER", sccache);
        }
        if self.time_passes {
            command.arg("-Ztime-passes");
        }
        let cargo_output = command.output()?;
        if cargo_output.status.success() {
            if self.time_passes {
                let stdout = String::from_utf8_lossy(&cargo_output.stdout);
                eprintln!("{}", stdout);
            }
        } else {
            let stderr = String::from_utf8_lossy(&cargo_output.stderr);
            let stdout = String::from_utf8_lossy(&cargo_output.stdout);
            let mut non_json_error = None;
            let errors: Vec<CompilationError> = stderr
                .lines()
                .chain(stdout.lines())
                .filter_map(|line| {
                    json::parse(&line)
                        .ok()
                        .and_then(|json| CompilationError::opt_new(json, code_block))
                        .or_else(|| {
                            if KNOWN_NON_JSON_ERRORS.is_match(line) {
                                non_json_error = Some(line);
                            }
                            None
                        })
                })
                .collect();
            if errors.is_empty() {
                if let Some(error) = non_json_error {
                    bail!(Error::Message(error.to_owned()));
                } else {
                    bail!(Error::Message(format!(
                        "Compilation failed, but no parsable errors were found. STDERR:\n\
                         {}\nSTDOUT:{}\n",
                        stderr, stdout
                    )));
                }
            } else {
                bail!(Error::CompilationErrors(errors));
            }
        }
        self.build_num += 1;
        let copied_so_file = self
            .deps_dir()
            .join(shared_object_name_from_crate_name(&format!(
                "code_{}",
                self.build_num
            )));
        // Every time we compile, the output file is the same. We need to
        // renamed it so that we have a unique filename, otherwise we wouldn't
        // be able to load the result of the next compilation. Also, on Windows,
        // a loaded dll gets locked, so we couldn't even compile a second time
        // if we didn't load a different file.
        rename_or_copy_so_file(&self.so_path(), &copied_so_file)?;
        Ok(SoFile {
            path: copied_so_file,
        })
    }

    fn get_cargo_toml_contents(&self, eval_context: &EvalContext) -> String {
        let crate_imports = eval_context.format_cargo_deps();
        format!(
            r#"
[package]
name = "{}"
version = "1.0.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[profile.dev]
opt-level = {}
debug = false
rpath = true
lto = false
debug-assertions = true
codegen-units = 16
panic = 'unwind'
incremental = true
overflow-checks = true

[dependencies]
{}
"#,
            CRATE_NAME,
            eval_context.opt_level(),
            crate_imports
        )
    }
}

pub(crate) struct SoFile {
    pub(crate) path: PathBuf,
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate irc;

use std::net::TcpStream;
use std::io::{self, Write, BufRead, BufReader};
use std::thread;
use std::env;

use irc::protocol::message::{IrcMessage, Message, Body};
use irc::protocol::command::{Command, UserCommand, NickCommand, JoinCommand, PongCommand, PrivmsgCommand, QuitCommand};
use irc::protocol::reply::ReplyCode;

fn main() {
    env_logger::init().unwrap();

    let username: String = env::args().skip(1).next().expect("You have to provide a username").clone();
    let server_addr = "irc.mozilla.org:6667";

    println!("Logging to {:?} as {:?}", server_addr, username);

    info!("Connecting to {:?}", server_addr);
    let mut stream = TcpStream::connect(server_addr).unwrap();
    let bufreader = BufReader::new(stream.try_clone().unwrap());

    let nickname = &username[..];

    {
        let usercmd = UserCommand::new(&username[..], 0, &username[..]);
        let nickcmd = NickCommand::new(nickname);
        let usermsg = Message::new(None, Body::command(usercmd));
        let nickmsg = Message::new(None, Body::command(nickcmd));

        info!("Authorizing: sending {:?}", usermsg);
        write!(stream, "{}\r\n", usermsg).unwrap();

        info!("Authorizing: sending {:?}", nickmsg);
        write!(stream, "{}\r\n", nickmsg).unwrap();
    }

    let mut lines = bufreader.lines();

    {
        // Waiting for MOTD_START
        while let Some(line) = lines.next() {
            let line = line.unwrap();

            let parsed = Message::from_str(line.trim()).unwrap();
            trace!("{:?}", parsed);

            if let &Body::Reply(ref rpl) = parsed.body() {
                if let ReplyCode::RPL_MOTDSTART = rpl.code() {
                    let params = rpl.parameters().collect::<Vec<&str>>();
                    println!("-*- {}", params.connect(" "));
                    break;
                } else if let ReplyCode::ERR_NICKNAMEINUSE = rpl.code() {
                    panic!("Nickname {:?} already in use", nickname);
                }
            } else if let &Body::Command(ref cmd) = parsed.body() {
                if let &Command::Notice(ref notice) = cmd {
                    println!("-*- Notice: {}", notice.message());
                }
            }
        }

        // Printing MOTD
        while let Some(line) = lines.next() {
            let line = line.unwrap();

            let parsed = Message::from_str(line.trim()).unwrap();
            trace!("{:?}", parsed);

            if let &Body::Reply(ref rpl) = parsed.body() {
                match rpl.code() {
                    ReplyCode::RPL_MOTD => {
                        let params = rpl.parameters().collect::<Vec<&str>>();
                        println!("-*- {}", params.connect(" "));
                    },
                    ReplyCode::RPL_ENDOFMOTD => {
                        let params = rpl.parameters().collect::<Vec<&str>>();
                        println!("-*- {}", params.connect(" "));
                        break;
                    },
                    _ => {}
                }
            }
        }
    }

    {
        // Join channel
        let joincmd = JoinCommand::new("#rust", None);
        let joinmsg = Message::new(None, Body::command(joincmd));

        info!("Join channel: Sending {:?}", joinmsg);
        write!(stream, "{}\r\n", joinmsg).unwrap();
    }

    let mut writeable_stream = stream.try_clone().unwrap();
    let cloned_username = username.clone();
    thread::spawn(move|| {
        let bufstdin = BufReader::new(io::stdin());
        for line in bufstdin.lines() {
            let line = line.unwrap();
            let msg = line.trim();

            if !msg.is_empty() {
                if msg.starts_with("/") {
                    let mut sp = msg.splitn(2, ' ');
                    let cmd = sp.next().unwrap();
                    let cmd = (&cmd[1..]).chars().flat_map(|x| x.to_uppercase()).collect::<String>();

                    match &cmd[..] {
                        "QUIT" => {
                            let quitcmd = QuitCommand::new(sp.next());
                            let msg = Message::new(None, Body::command(quitcmd));
                            trace!("Quit: Sending {:?}", msg);
                            write!(writeable_stream, "{}\r\n", msg).unwrap();
                        },
                        _ => {
                            error!("Unknown command {}", cmd);
                        }
                    }
                } else {
                    let privmsg = PrivmsgCommand::new("#rust", msg);
                    println!("-*- < {} > {}", cloned_username, msg);
                    let msg = Message::new(None, Body::command(privmsg));
                    trace!("Privmsg: Sending {:?}", msg);
                    write!(writeable_stream, "{}\r\n", msg).unwrap();
                }
            }
        }
    });

    // Prints conversation
    for line in lines {
        let line = line.unwrap();

        let parsed = Message::from_str(line.trim()).unwrap();
        trace!("{:?}", parsed);

        if let &Body::Command(ref cmd) = parsed.body() {
            match cmd {
                &Command::Privmsg(ref privmsg) => {
                    let prefix = parsed.prefix().unwrap();

                    let name = match prefix.find('!') {
                        None => prefix,
                        Some(idx) => &prefix[..idx],
                    };

                    println!("-*- < {} > {}", name, privmsg.message());
                },
                &Command::Ping(ref ping) => {
                    debug!("Received ping {:?}", ping);
                    let pong = PongCommand::new(ping.servers().get());
                    debug!("Sending pong {:?}", pong);
                    write!(stream, "{}\r\n", Message::new(None, Body::command(pong))).unwrap();
                },
                _ => {}
            }
        }
    }
}
<commit_msg>slightly update<commit_after>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate irc;

use std::net::TcpStream;
use std::io::{self, Write, BufRead, BufReader};
use std::thread;
use std::env;

use irc::protocol::message::{IrcMessage, Message, Body};
use irc::protocol::command::{Command, UserCommand, NickCommand, JoinCommand, PongCommand, PrivmsgCommand, QuitCommand};
use irc::protocol::reply::ReplyCode;

fn main() {
    env_logger::init().unwrap();

    let username: String = env::args().skip(1).next().expect("You have to provide a username").clone();
    let server_addr = "irc.mozilla.org:6667";

    println!("Logging to {:?} as {:?}", server_addr, username);

    info!("Connecting to {:?}", server_addr);
    let mut stream = TcpStream::connect(server_addr).unwrap();
    let bufreader = BufReader::new(stream.try_clone().unwrap());

    let nickname = &username[..];

    {
        let usercmd = UserCommand::new(&username[..], 0, &username[..]);
        let nickcmd = NickCommand::new(nickname);
        let usermsg = Message::new(None, Body::command(usercmd));
        let nickmsg = Message::new(None, Body::command(nickcmd));

        info!("Authorizing: sending {:?}", usermsg);
        write!(stream, "{}\r\n", usermsg).unwrap();

        info!("Authorizing: sending {:?}", nickmsg);
        write!(stream, "{}\r\n", nickmsg).unwrap();
    }

    let mut lines = bufreader.lines();

    {
        // Waiting for MOTD_START
        while let Some(line) = lines.next() {
            let line = line.unwrap();

            let parsed = Message::from_str(line.trim()).unwrap();
            trace!("{:?}", parsed);

            if let &Body::Reply(ref rpl) = parsed.body() {
                if let ReplyCode::RPL_MOTDSTART = rpl.code() {
                    let params = rpl.parameters().collect::<Vec<&str>>();
                    println!("-*- {}", params.connect(" "));
                    break;
                } else if let ReplyCode::ERR_NICKNAMEINUSE = rpl.code() {
                    panic!("Nickname {:?} already in use", nickname);
                }
            } else if let &Body::Command(ref cmd) = parsed.body() {
                if let &Command::Notice(ref notice) = cmd {
                    println!("-*- Notice: {}", notice.message());
                }
            }
        }

        // Printing MOTD
        while let Some(line) = lines.next() {
            let line = line.unwrap();

            let parsed = Message::from_str(line.trim()).unwrap();
            trace!("{:?}", parsed);

            if let &Body::Reply(ref rpl) = parsed.body() {
                match rpl.code() {
                    ReplyCode::RPL_MOTD => {
                        let params = rpl.parameters().collect::<Vec<&str>>();
                        println!("-*- {}", params.connect(" "));
                    },
                    ReplyCode::RPL_ENDOFMOTD => {
                        let params = rpl.parameters().collect::<Vec<&str>>();
                        println!("-*- {}", params.connect(" "));
                        break;
                    },
                    _ => {}
                }
            }
        }
    }

    {
        // Join channel
        let joincmd = JoinCommand::new("#rust", None);
        let joinmsg = Message::new(None, Body::command(joincmd));

        info!("Join channel: Sending {:?}", joinmsg);
        write!(stream, "{}\r\n", joinmsg).unwrap();
    }

    let mut writeable_stream = stream.try_clone().unwrap();
    let cloned_username = username.clone();
    thread::spawn(move|| {
        let bufstdin = BufReader::new(io::stdin());
        for line in bufstdin.lines() {
            let line = line.unwrap();
            let msg = line.trim();

            if !msg.is_empty() {
                if msg.starts_with("/") {
                    let mut sp = msg.splitn(2, ' ');
                    let cmd = sp.next().unwrap();
                    let cmd = (&cmd[1..]).chars().flat_map(|x| x.to_uppercase()).collect::<String>();

                    match &cmd[..] {
                        "QUIT" => {
                            let quitcmd = QuitCommand::new(sp.next());
                            let msg = Message::new(None, Body::command(quitcmd));
                            trace!("Quit: Sending {:?}", msg);
                            write!(writeable_stream, "{}\r\n", msg).unwrap();
                        },
                        _ => {
                            let privmsg = PrivmsgCommand::new("#rust", msg);
                            println!("-*- < {} > {}", cloned_username, msg);
                            let msg = Message::new(None, Body::command(privmsg));
                            trace!("Privmsg: Sending {:?}", msg);
                            write!(writeable_stream, "{}\r\n", msg).unwrap();
                        }
                    }
                } else {
                    let privmsg = PrivmsgCommand::new("#rust", msg);
                    println!("-*- < {} > {}", cloned_username, msg);
                    let msg = Message::new(None, Body::command(privmsg));
                    trace!("Privmsg: Sending {:?}", msg);
                    write!(writeable_stream, "{}\r\n", msg).unwrap();
                }
            }
        }
    });

    // Prints conversation
    for line in lines {
        let line = line.unwrap();

        let parsed = Message::from_str(line.trim()).unwrap();
        trace!("{:?}", parsed);

        if let &Body::Command(ref cmd) = parsed.body() {
            match cmd {
                &Command::Privmsg(ref privmsg) => {
                    let prefix = parsed.prefix().unwrap();

                    let name = match prefix.find('!') {
                        None => prefix,
                        Some(idx) => &prefix[..idx],
                    };

                    println!("-*- < {} > {}", name, privmsg.message());
                },
                &Command::Ping(ref ping) => {
                    debug!("Received ping {:?}", ping);
                    let pong = PongCommand::new(ping.servers().get());
                    debug!("Sending pong {:?}", pong);
                    write!(stream, "{}\r\n", Message::new(None, Body::command(pong))).unwrap();
                },
                _ => {}
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate crossbeam;
extern crate clap;
#[macro_use] extern crate version;
#[macro_use] extern crate log;
extern crate walkdir;

extern crate libimagrt;
extern crate libimagerror;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;

use walkdir::WalkDir;
use crossbeam::*;
use clap::{Arg, AppSettings, SubCommand};

use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;

fn help_text(cmds: Vec<String>) -> String {
    let text = format!(r#"

     _
    (_)_ __ ___   __ _  __ _
    | | '_ \` _ \/ _\`|/ _\`|
    | | | | | | | (_| | (_| |
    |_|_| |_| |_|\__,_|\__, |
                       |___/
    -------------------------

    Usage: imag [--version | --versions | -h | --help] <command> <args...>

    imag - the personal information management suite for the commandline

    imag is a PIM suite for the commandline. It consists of several commands,
    called "modules". Each module implements one PIM aspect and all of these
    modules can be used independently.

    Available commands:

    {imagbins}

    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1

    (c) 2016 Matthias Beyer and contributors"#, imagbins = cmds.into_iter()
        .map(|cmd| format!("\t{}\n", cmd))
        .fold(String::new(), |s, c| {
            let s = s + c.as_str();
            s
        }));
    text
}

fn get_commands() -> Vec<String> {
    let path = env::var("PATH");
    if path.is_err() {
        println!("PATH error: {:?}", path);
        exit(1);
    }
    let pathelements = path.unwrap();
    let pathelements = pathelements.split(":");

    let joinhandles : Vec<ScopedJoinHandle<Vec<String>>> = pathelements
        .map(|elem| {
            crossbeam::scope(|scope| {
                scope.spawn(|| {
                    WalkDir::new(elem)
                        .max_depth(1)
                        .into_iter()
                        .filter(|path| {
                            match path {
                                &Ok(ref p) => p.file_name()
                                    .to_str()
                                    .map_or(false, |filename| filename.starts_with("imag-")),
                                &Err(_)   => false,
                            }
                        })
                        .filter_map(|x| x.ok())
                        .filter_map(|path| {
                           path.file_name()
                               .to_str()
                               .and_then(|s| s.splitn(2, "-").nth(1).map(String::from))
                        })
                        .collect()
                })
            })
        })
        .collect();

    let mut execs = vec![];
    for joinhandle in joinhandles.into_iter() {
        let mut v = joinhandle.join();
        execs.append(&mut v);
    }

    execs
}

fn main() {
    let appname  = "imag";
    let version  = &version!();
    let about    = "imag - the PIM suite for the commandline";
    let commands = get_commands();
    let helptext = help_text(commands);
    let app      = Runtime::get_default_cli_builder(appname, version, about)
        .settings(&[AppSettings::AllowExternalSubcommands])
        .arg(Arg::with_name("version")
             .long("version")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the version of imag"))
        .arg(Arg::with_name("versions")
             .long("versions")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the versions of the imag commands"))
        .subcommand(SubCommand::with_name("help").help("Show help"))
        .help(helptext.as_str());
    let rt = Runtime::new(app)
        .unwrap_or_else(|e| {
            println!("Runtime couldn't be setup. Exiting");
            trace_error(&e);
            exit(1);
        });
    let matches = rt.cli();

    debug!("matches: {:?}", matches);

    if matches.is_present("version") {
        debug!("Showing version");
        println!("imag {}", &version!()[..]);
        exit(0);
    }

    if matches.is_present("versions") {
        debug!("Showing versions");
        let mut result = vec![];
        for command in get_commands().iter() {
            result.push(crossbeam::scope(|scope| {
                scope.spawn(|| {
                    let v = Command::new(command).arg("--version").output();
                    match v {
                        Ok(v) => match String::from_utf8(v.stdout) {
                            Ok(s) => format!("{} -> {}", command, s),
                            Err(e) => format!("Failed calling {} -> {:?}", command, e),
                        },
                        Err(e) => format!("Failed calling {} -> {:?}", command, e),
                    }
                })
            }))
        }

        for versionstring in result.into_iter().map(|handle| handle.join()) {
            println!("{}", versionstring);
        }
    }

    match matches.subcommand() {
        (subcommand, Some(scmd)) => {
            let subcommand_args : Vec<&str> = scmd.values_of("").unwrap().collect();
            debug!("Calling 'imag-{}' with args: {:?}", subcommand, subcommand_args);

            match Command::new(format!("imag-{}", subcommand))
                .stdin(Stdio::inherit())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .args(&subcommand_args[..])
                .spawn()
                .and_then(|mut handle| handle.wait())
            {
                Ok(exit_status) => {
                    if !exit_status.success() {
                        debug!("{} exited with non-zero exit code: {:?}", subcommand, exit_status);
                        println!("{} exited with non-zero exit code", subcommand);
                        exit(exit_status.code().unwrap_or(42));
                    }
                    debug!("Successful exit!");
                },

                Err(e) => {
                    debug!("Error calling the subcommand");
                    match e.kind() {
                        ErrorKind::NotFound => {
                            println!("No such command: 'imag-{}'", subcommand);
                            println!("See 'imag --help' for available subcommands");
                            exit(2);
                        },
                        ErrorKind::PermissionDenied => {
                            println!("No permission to execute: 'imag-{}'", subcommand);
                            exit(1);
                        },
                        _ => {
                            println!("Error spawning: {:?}", e);
                            exit(1337);
                        }
                    }
                }
            }
        },
        // clap ensures we have valid input by exiting if not.
        // The above case is a catch-all for subcommands,
        // so nothing else needs to be expexted.
        _ => unreachable!(),
    }
}
<commit_msg>Fix panics due to unwrap on Option::None<commit_after>extern crate crossbeam;
extern crate clap;
#[macro_use] extern crate version;
#[macro_use] extern crate log;
extern crate walkdir;

extern crate libimagrt;
extern crate libimagerror;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;

use walkdir::WalkDir;
use crossbeam::*;
use clap::{Arg, AppSettings, SubCommand};

use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;

fn help_text(cmds: Vec<String>) -> String {
    let text = format!(r#"

     _
    (_)_ __ ___   __ _  __ _
    | | '_ \` _ \/ _\`|/ _\`|
    | | | | | | | (_| | (_| |
    |_|_| |_| |_|\__,_|\__, |
                       |___/
    -------------------------

    Usage: imag [--version | --versions | -h | --help] <command> <args...>

    imag - the personal information management suite for the commandline

    imag is a PIM suite for the commandline. It consists of several commands,
    called "modules". Each module implements one PIM aspect and all of these
    modules can be used independently.

    Available commands:

    {imagbins}

    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1

    (c) 2016 Matthias Beyer and contributors"#, imagbins = cmds.into_iter()
        .map(|cmd| format!("\t{}\n", cmd))
        .fold(String::new(), |s, c| {
            let s = s + c.as_str();
            s
        }));
    text
}

fn get_commands() -> Vec<String> {
    let path = env::var("PATH");
    if path.is_err() {
        println!("PATH error: {:?}", path);
        exit(1);
    }
    let pathelements = path.unwrap();
    let pathelements = pathelements.split(":");

    let joinhandles : Vec<ScopedJoinHandle<Vec<String>>> = pathelements
        .map(|elem| {
            crossbeam::scope(|scope| {
                scope.spawn(|| {
                    WalkDir::new(elem)
                        .max_depth(1)
                        .into_iter()
                        .filter(|path| {
                            match path {
                                &Ok(ref p) => p.file_name()
                                    .to_str()
                                    .map_or(false, |filename| filename.starts_with("imag-")),
                                &Err(_)   => false,
                            }
                        })
                        .filter_map(|x| x.ok())
                        .filter_map(|path| {
                           path.file_name()
                               .to_str()
                               .and_then(|s| s.splitn(2, "-").nth(1).map(String::from))
                        })
                        .collect()
                })
            })
        })
        .collect();

    let mut execs = vec![];
    for joinhandle in joinhandles.into_iter() {
        let mut v = joinhandle.join();
        execs.append(&mut v);
    }

    execs
}

fn main() {
    let appname  = "imag";
    let version  = &version!();
    let about    = "imag - the PIM suite for the commandline";
    let commands = get_commands();
    let helptext = help_text(commands);
    let app      = Runtime::get_default_cli_builder(appname, version, about)
        .settings(&[AppSettings::AllowExternalSubcommands])
        .arg(Arg::with_name("version")
             .long("version")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the version of imag"))
        .arg(Arg::with_name("versions")
             .long("versions")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the versions of the imag commands"))
        .subcommand(SubCommand::with_name("help").help("Show help"))
        .help(helptext.as_str());
    let rt = Runtime::new(app)
        .unwrap_or_else(|e| {
            println!("Runtime couldn't be setup. Exiting");
            trace_error(&e);
            exit(1);
        });
    let matches = rt.cli();

    debug!("matches: {:?}", matches);

    if matches.is_present("version") {
        debug!("Showing version");
        println!("imag {}", &version!()[..]);
        exit(0);
    }

    if matches.is_present("versions") {
        debug!("Showing versions");
        let mut result = vec![];
        for command in get_commands().iter() {
            result.push(crossbeam::scope(|scope| {
                scope.spawn(|| {
                    let v = Command::new(command).arg("--version").output();
                    match v {
                        Ok(v) => match String::from_utf8(v.stdout) {
                            Ok(s) => format!("{} -> {}", command, s),
                            Err(e) => format!("Failed calling {} -> {:?}", command, e),
                        },
                        Err(e) => format!("Failed calling {} -> {:?}", command, e),
                    }
                })
            }))
        }

        for versionstring in result.into_iter().map(|handle| handle.join()) {
            println!("{}", versionstring);
        }
    }

    match matches.subcommand() {
        (subcommand, Some(scmd)) => {
            debug!("Calling with subcommand: {}", subcommand);
            let subcommand_args : Vec<&str> = match scmd.values_of("") {
                Some(values) => values.collect(),
                None => Vec::new()
            };
            debug!("Calling 'imag-{}' with args: {:?}", subcommand, subcommand_args);

            match Command::new(format!("imag-{}", subcommand))
                .stdin(Stdio::inherit())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .args(&subcommand_args[..])
                .spawn()
                .and_then(|mut handle| handle.wait())
            {
                Ok(exit_status) => {
                    if !exit_status.success() {
                        debug!("{} exited with non-zero exit code: {:?}", subcommand, exit_status);
                        println!("{} exited with non-zero exit code", subcommand);
                        exit(exit_status.code().unwrap_or(42));
                    }
                    debug!("Successful exit!");
                },

                Err(e) => {
                    debug!("Error calling the subcommand");
                    match e.kind() {
                        ErrorKind::NotFound => {
                            println!("No such command: 'imag-{}'", subcommand);
                            println!("See 'imag --help' for available subcommands");
                            exit(2);
                        },
                        ErrorKind::PermissionDenied => {
                            println!("No permission to execute: 'imag-{}'", subcommand);
                            exit(1);
                        },
                        _ => {
                            println!("Error spawning: {:?}", e);
                            exit(1337);
                        }
                    }
                }
            }
        },
        // clap ensures we have valid input by exiting if not.
        // The above case is a catch-all for subcommands,
        // so nothing else needs to be expexted.
        _ => unreachable!(),
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

// https://leetcode.com/problems/the-number-of-weak-characters-in-the-game/
pub fn number_of_weak_characters(mut properties: Vec<Vec<i32>>) -> i32 {
    let mut map: BTreeMap<i32, Vec<i32>> = BTreeMap::new();
    for prop in properties.iter() {
        map.entry(prop[0]).or_insert(vec![]).push(prop[1]);
    }
    let mut suffix_defense = vec![0; map.len()];
    for (i, (_, defenses)) in map.iter().enumerate().rev() {
        let max_defense = defenses.iter().max().unwrap();
        if i == map.len() - 1 {
            suffix_defense[i] = *max_defense;
        } else {
            suffix_defense[i] = *max_defense.max(&suffix_defense[i + 1]);
        }
    }
    let mut answer = 0;
    for (i, (_, defenses)) in map.iter().enumerate() {
        for defense in defenses.iter() {
            if i + 1 < map.len() && *defense < suffix_defense[i + 1] {
                answer += 1;
            }
        }
    }
    answer
}

fn main() {
    println!(
        "{}",
        number_of_weak_characters(vec![vec![5, 5], vec![6, 3], vec![3, 6]])
    ); // 0
    println!(
        "{}",
        number_of_weak_characters(vec![vec![2, 2], vec![3, 3]])
    ); // 1
    println!(
        "{}",
        number_of_weak_characters(vec![vec![1, 5], vec![10, 4], vec![4, 3]])
    ); // 1
    println!(
        "{}",
        number_of_weak_characters(vec![vec![1, 5], vec![10, 4], vec![4, 3], vec![5, 5]])
    ); // 1
    println!(
        "{}",
        number_of_weak_characters(vec![
            vec![7, 9],
            vec![10, 7],
            vec![6, 9],
            vec![10, 4],
            vec![7, 5],
            vec![7, 10]
        ])
    ); // 2
}
<commit_msg>Solve problem 1996<commit_after>use std::collections::BTreeMap;

// https://leetcode.com/problems/the-number-of-weak-characters-in-the-game/
pub fn number_of_weak_characters(properties: Vec<Vec<i32>>) -> i32 {
    let mut map: BTreeMap<i32, Vec<i32>> = BTreeMap::new();
    for prop in properties.iter() {
        map.entry(prop[0]).or_insert(vec![]).push(prop[1]);
    }
    let mut answer = 0;
    let mut max = 0;
    for (i, (_, defenses)) in map.iter().enumerate().rev() {
        if i == map.len() - 1 {
            max = *defenses.iter().max().unwrap();
        } else {
            let mut tmp_max = 0;
            for defense in defenses.iter() {
                if *defense < max {
                    answer += 1;
                }
                tmp_max = tmp_max.max(*defense);
            }
            max = max.max(tmp_max);
        }
    }
    answer
}

fn main() {
    println!(
        "{}",
        number_of_weak_characters(vec![vec![5, 5], vec![6, 3], vec![3, 6]])
    ); // 0
    println!(
        "{}",
        number_of_weak_characters(vec![vec![2, 2], vec![3, 3]])
    ); // 1
    println!(
        "{}",
        number_of_weak_characters(vec![vec![1, 5], vec![10, 4], vec![4, 3]])
    ); // 1
    println!(
        "{}",
        number_of_weak_characters(vec![vec![1, 5], vec![10, 4], vec![4, 3], vec![5, 5]])
    ); // 1
    println!(
        "{}",
        number_of_weak_characters(vec![
            vec![7, 9],
            vec![10, 7],
            vec![6, 9],
            vec![10, 4],
            vec![7, 5],
            vec![7, 10]
        ])
    ); // 2
}
<|endoftext|>