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
// The author disclaims copyright to this source code.  In place of
// a legal notice, here is a blessing:
//    May you do good and not evil.
//    May you find forgiveness for yourself and forgive others.
//    May you share freely, never taking more than you give.

//! This is a Rust implementation of the [Damm algorithm](https://en.wikipedia.org/wiki/Damm_algorithm).
//! The algorithm is used for ID validation to catch
//! common transposition errors.
//!
//! This crate provides a simple and fast implementation
//! of the Damm algorithm using `u128` as the type of the id.
//! The only downside to this approach is that the largest ID
//! that one can use is `340282366920938463463374607431768211455`,
//! which means that the largest number of digits that one can use
//! _reliably_ is 38.
//!
//! If for some reason this is a major issue for someone needing any
//! more digits than that, open an issue at gitlab and I'll extend this
//! library to provide a string representation for Really Long ID's™.

extern crate digits_iterator;

use digits_iterator::DigitsExtension;

// usize to avoid casting everywhere (elements of this
//   are used as indicies)
const OP_TABLE: [[usize; 10]; 10] = [
    [0, 3, 1, 7, 5, 9, 8, 6, 4, 2],
    [7, 0, 9, 2, 1, 5, 4, 8, 6, 3],
    [4, 2, 0, 6, 8, 7, 1, 3, 5, 9],
    [1, 7, 5, 0, 9, 8, 3, 4, 2, 6],
    [6, 1, 2, 3, 0, 4, 5, 9, 7, 8],
    [3, 6, 7, 4, 2, 0, 9, 5, 8, 1],
    [5, 8, 6, 9, 7, 2, 0, 1, 3, 4],
    [8, 9, 4, 5, 3, 6, 2, 0, 1, 7],
    [9, 4, 3, 8, 6, 1, 7, 2, 0, 5],
    [2, 5, 8, 1, 4, 3, 6, 7, 9, 0]
];

// Used for both generation and verification
fn check_digit(bare_id: u128) -> usize {
    let mut interim = 0;
    for digit in bare_id.digits() {
        interim = OP_TABLE[interim][digit as usize];
    }
    interim
}

/// Is an ID valid?
///
/// # Example
/// ```rust
/// # use damm;
/// if damm::verify(928374629172) {
///     // ... (will execute)
/// }
/// # else {
/// #     panic!();
/// # }
pub fn verify(id: u128) -> bool {
    check_digit(id) == 0
}

/// Append a checksum to an existing ID
///
/// # Example
/// ```rust
/// # use damm;
/// // Could be a random number
/// let id = damm::id(8293); // 82931
/// ```
pub fn id(bare_id: u128) -> u128 {
    let sum = check_digit(bare_id);
    (bare_id * 10) + sum as u128
}

#[cfg(test)]
mod tests {
    use *;
    
    // This id is pulled from wikipedia
    // The others were generated using this library (bad)
    #[test]
    fn _572() {
        assert_eq!(check_digit(572), 4);
        assert_eq!(id(572), 5724);
        assert!(verify(5724));
    }
    
    #[test]
    fn _8293() {
        assert_eq!(check_digit(8293), 1);
        assert_eq!(id(8293), 82931);
        assert!(verify(82931));
    }
    
    #[test]
    fn _92837462917() {
        assert_eq!(check_digit(92837462917), 2);
        assert_eq!(id(92837462917), 928374629172);
        assert!(verify(928374629172));
    }
}