summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 9243c1f2e8117030b2ba39e01e6a3f39fc2c88ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
extern crate clap;
extern crate rand;

use clap::{Arg, App};
use std::collections::vec_deque::VecDeque;
use std::io::{self, BufRead};
use std::result::Result;

fn some<R: rand::Rng, T: Iterator<Item = Result<String, io::Error>>>(
    r: &mut R,
    n: usize,
    lines: T,
) -> Result<VecDeque<String>, io::Error> {
    let mut result: VecDeque<String> = VecDeque::with_capacity(n);
    fn push(v: &mut VecDeque<String>, n: usize, x: String) {
        v.push_back(x);
        if v.len() > n {
            v.pop_front();
        }
    };

    for (i, line) in lines.enumerate() {
        let line = line?;
        if result.len() < n {
            push(&mut result, n, line);
        } else if r.gen_range(0, i) < n {
            let victim = r.gen_range(0, n);
            result.remove(victim);
            push(&mut result, n, line);

            // Assumption: |result| contains m random elements from the (i-1)
            // first lines.
            //
            // |line| is in result with n/i probability (if expression)
        }
        // An arbitrary line0 occuring before line was in result with
        // probability m/(i-1).
        //
        // P(line0 is still in result) =
        //   m/i * (1 - 1/m) + (1 - m/i) =
        //   m/i - 1/i + 1 - m/i = 1 - 1/i = (i-1) / i
        //
        // The probability that an arbitrary line from the i first lines is in
        // |result| is: m/(i-1) * ((i-1)/i) = m/i. The induction holds.
    }
    return Ok(result);
}

fn main() {
    let matches = App::new("some")
        .version("0.1")
        .author("Kjetil Ørbekk <kj@orbekk.com>")
        .arg(
            Arg::with_name("lines")
                .short("n")
                .value_name("NUM")
                .help("Number of lines to print")
                .takes_value(true),
        )
        .get_matches();
    let num_lines = matches
        .value_of("lines")
        .unwrap_or("10")
        .parse::<usize>()
        .expect("number of lines to print");
    let stdin = io::stdin();
    let lines = some(&mut rand::thread_rng(), num_lines, stdin.lock().lines());
    match lines {
        Ok(lines) => {
            for line in lines {
                println!("{}", line);
            }
        }
        Err(e) => println!("Error: {}", e),
    }
}