summaryrefslogtreecommitdiff
path: root/rust/huffman/src/main.rs
blob: d2fc8a18395aaee10b5c7de096c167a22a74e6a3 (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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
extern crate itertools;
use itertools::Itertools;
use std::cmp::Eq;
use std::cmp::Ordering;
use std::collections::binary_heap::BinaryHeap;

static MESSAGE: &'static str = r#"
  Hello! What's going on? This is a program.
  The brown fox jumped over the lazy dog.
  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  \o/
"#;

#[derive(Debug, Eq, PartialEq)]
enum Tree<T> {
    Leaf(i32, T),
    Branch(i32, Box<Tree<T>>, Box<Tree<T>>),
}

#[derive(Debug, Eq, PartialEq)]
enum Direction { Left, Right }

fn cost<T>(t: &Tree<T>) -> i32 {
    match *t {
        Tree::Leaf(cost, _) => cost,
        Tree::Branch(cost, _, _) => cost,
    }
}

impl<T: Eq> Ord for Tree<T> {
    fn cmp(&self, other: &Self) -> Ordering {
        cost(other).cmp(&cost(self))
    }
}

impl<T: Eq> PartialOrd for Tree<T> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

fn make_path<T: Eq>(tree: &Tree<T>, elem: &T) -> Vec<Direction> {
    fn find<T: Eq>(n: &Tree<T>, elem: &T) -> Option<Vec<Direction>> {
        match *n {
            Tree::Leaf(_, ref e) =>
                if e == elem { Some(vec!()) } else { None },
            Tree::Branch(_, ref left, ref right) =>
                find(left.as_ref(), elem)
                .map(|mut path| { path.push(Direction::Left); path })

                .or(find(right.as_ref(), elem)
                    .map(|mut path| { path.push(Direction::Right); path})),
        }
    };
    let mut result = find(tree, elem).unwrap();
    result.reverse();
    result
}

fn find_char<T: Eq+Copy>(tree: &Tree<T>, path: &[Direction]) -> Option<T> {
    match (tree, path.split_first()) {
        (&Tree::Branch(_, ref left, _), Some((&Direction::Left, ds))) =>
            find_char(left.as_ref(), ds),
        (&Tree::Branch(_, _, ref right), Some((&Direction::Right, ds))) =>
            find_char(right.as_ref(), ds),
        (&Tree::Leaf(_, ref c), None) => Some(*c),
        _ => None
    }
}

fn main() {
    if MESSAGE.is_empty() {
        panic!("Empty string");
    }

    let mut counts = MESSAGE
        .chars()
        .sorted().into_iter()
        .group_by(|x| *x)
        .map(|(elem, xs)| (elem, xs.len() as i32))
        .map(|(elem, cost)| Box::new(Tree::Leaf(cost, elem)))
        .collect::<BinaryHeap<_>>();

    let mut left;
    loop {
        left = counts.pop().unwrap();
        let right = match counts.pop() {
            Some(right) => right,
            None => break
        };

        counts.push(Box::new(
            Tree::Branch(cost(left.as_ref()) + cost(right.as_ref()),
                         left, right)));
    }

    let restored_message = MESSAGE
        .chars()
        .map(|c| make_path(left.as_ref(), &c))
        .flat_map(|p| find_char(left.as_ref(), &p))
        .collect::<String>();

    println!("{}", MESSAGE);
    println!("{}", restored_message);
}