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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use std::cmp;

#[derive(Debug, PartialEq)]
pub enum DiffResult<T: PartialEq + Clone> {
    Removed(DiffElement<T>),
    Common(DiffElement<T>),
    Added(DiffElement<T>),
}

#[derive(Debug, PartialEq)]
pub struct DiffElement<T: PartialEq + Clone> {
    old_index: Option<usize>,
    new_index: Option<usize>,
    data: T,
}

fn create_table<T: PartialEq + Clone>(old: &[T], new: &[T]) -> Vec<Vec<u32>> {
    let new_len = new.len();
    let old_len = old.len();
    let mut table = vec![vec![0; old_len + 1]; new_len + 1];
    for (n, _) in new.iter().enumerate() {
        let n = new_len - n - 1;
        table[n][old_len] = 0;
        for (o, _) in old.iter().enumerate() {
            let o = old_len - o - 1;
            if new[n] == old[o] {
                table[n][o] = table[n + 1][o + 1] + 1;
            } else {
                table[n][o] = cmp::max(table[n + 1][o], table[n][o + 1]);
            }
        }
    }
    table
}

pub fn diff<T: PartialEq + Clone>(old: &[T], new: &[T]) -> Vec<DiffResult<T>> {
    let table = create_table(old, new);
    let mut n = 0;
    let mut o = 0;
    let mut result: Vec<DiffResult<T>> = Vec::new();
    let new_len = new.len();
    let old_len = old.len();

    loop {
        if new[n] == old[o] {
            result.push(DiffResult::Common(DiffElement {
                                               old_index: Some(o),
                                               new_index: Some(n),
                                               data: new[n].clone(),
                                           }));
            n += 1;
            o += 1;
        } else if table[n + 1][o] >= table[n][o + 1] {
            result.push(DiffResult::Added(DiffElement {
                                            old_index: None,
                                            new_index: Some(n),
                                            data: new[n].clone(),
                                        }));
            n += 1;
        } else {
            result.push(DiffResult::Removed(DiffElement {
                                            old_index: Some(o),
                                            new_index: None,
                                            data: old[o].clone(),
                                        }));
            o += 1;
        }
        if n >= new_len || o >= old_len {
            break;
        }
    }
    while n < new_len {
        result.push(DiffResult::Added(DiffElement {
                                        old_index: None,
                                        new_index: Some(n),
                                        data: new[n].clone(),
                                    }));
        n += 1;
    }
    while o < old_len {
        result.push(DiffResult::Removed(DiffElement {
                                        old_index: Some(o),
                                        new_index: None,
                                        data: old[o].clone(),
                                    }));
        o += 1;
    }
    result
}


#[test]
fn shoud_create_table_with_numbers() {
    let table = create_table(&vec![2, 3], &vec![0, 1, 2]);
    let expected = vec![vec![1, 0, 0], vec![1, 0, 0], vec![1, 0, 0], vec![0, 0, 0]];
    assert_eq!(table, expected);
}

#[test]
fn shoud_create_table_with_char() {
    let table = create_table(&vec!["a", "d"], &vec!["a", "b", "c"]);
    let expected = vec![vec![1, 0, 0], vec![0, 0, 0], vec![0, 0, 0], vec![0, 0, 0]];
    assert_eq!(table, expected);
}

#[test]
fn shoud_create_table_with_string() {
    let table = create_table(&vec!["abc", "bcd"], &vec!["abc", "bcd", "c"]);
    let expected = vec![vec![2, 1, 0], vec![1, 1, 0], vec![0, 0, 0], vec![0, 0, 0]];
    assert_eq!(table, expected);
}

#[test]
fn shoud_create_diff_result_with_added() {
    let result = diff(&vec!["abc", "c"], &vec!["abc", "bcd", "c"]);
    let expected = vec![DiffResult::Common(DiffElement {
                                               old_index: Some(0),
                                               new_index: Some(0),
                                               data: "abc",
                                           }),
                        DiffResult::Added(DiffElement {
                                            old_index: None,
                                            new_index: Some(1),
                                            data: "bcd",
                                        }),
                        DiffResult::Common(DiffElement {
                                               old_index: Some(1),
                                               new_index: Some(2),
                                               data: "c",
                                           })];

    assert_eq!(result, expected);
}


#[test]
fn shoud_create_diff_result_with_removed() {
    let result = diff(&vec!["abc", "bcd", "c"], &vec!["abc", "c"]);
    let expected = vec![DiffResult::Common(DiffElement {
                                               old_index: Some(0),
                                               new_index: Some(0),
                                               data: "abc",
                                           }),
                        DiffResult::Removed(DiffElement {
                                            old_index: Some(1),
                                            new_index: None,
                                            data: "bcd",
                                        }),
                        DiffResult::Common(DiffElement {
                                               old_index: Some(2),
                                               new_index: Some(1),
                                               data: "c",
                                           })];
    assert_eq!(result, expected);
}