R/Verhoeff.R

Defines functions VerifyCheckDigit.Verhoeff AppendCheckDigit.Verhoeff

Documented in AppendCheckDigit.Verhoeff VerifyCheckDigit.Verhoeff

#' Verhoeff algorithm implementation
#'
#' The Verhoeff algorithm is appropriate for numerical data and detects all
#' single-digit substitutions (x to y) and adjacent digit transpositions (xy to
#' yx). In addition, the Verhoeff check digit can detect most twin errors (xx to
#' yy), jump twin errors (xyx to zyz), jump transpositions (xyz to zyx), and
#' phonetic errors ("sixty" to "sixteen").
#'
#' @param x character vector of values
#'
#' @references Verhoeff, J. "Error Detecting Decimal Codes", Mathematical Centre
#'   Tract 29, The Mathematical Centre, Amsterdam, 1969.
#'
#' @name verhoeff
NULL

#' @rdname verhoeff
AppendCheckDigit.Verhoeff <- function(x) {

    x[is.na(x)] <- NA

    if (any(grepl("\\D", x))) {
        warning("Non-digit characters are disregarded in check digit calculation")
    }

    d <- matrix(
        c(
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
            1, 2, 3, 4, 0, 6, 7, 8, 9, 5,
            2, 3, 4, 0, 1, 7, 8, 9, 5, 6,
            3, 4, 0, 1, 2, 8, 9, 5, 6, 7,
            4, 0, 1, 2, 3, 9, 5, 6, 7, 8,
            5, 9, 8, 7, 6, 0, 4, 3, 2, 1,
            6, 5, 9, 8, 7, 1, 0, 4, 3, 2,
            7, 6, 5, 9, 8, 2, 1, 0, 4, 3,
            8, 7, 6, 5, 9, 3, 2, 1, 0, 4,
            9, 8, 7, 6, 5, 4, 3, 2, 1, 0
        ),
        ncol = 10,
        byrow = TRUE
    )

    p <- matrix(
        c(
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
            1, 5, 7, 6, 2, 8, 3, 0, 9, 4,
            5, 8, 0, 3, 7, 9, 6, 1, 4, 2,
            8, 9, 1, 6, 0, 4, 3, 5, 2, 7,
            9, 4, 5, 3, 1, 2, 6, 8, 7, 0,
            4, 2, 8, 6, 5, 7, 3, 9, 0, 1,
            2, 7, 9, 3, 8, 0, 6, 4, 1, 5,
            7, 0, 4, 6, 9, 1, 3, 2, 5, 8
        ),
        ncol = 10,
        byrow = TRUE
    )

    inv <- matrix(
        c(0, 4, 3, 2, 1, 5, 6, 7, 8, 9),
        ncol = 10,
        byrow = TRUE
    )

    y <- sapply(x, function(x) {
        y <- gsub("\\D", "", x)
        if (y %in% c("", NA, NaN)) {
            return(x)
        } else {
            c <- 0
            n <- as.integer(rev(unlist(strsplit(y, ""))))
            for (i in seq(n)) {
                c <- d[c + 1, p[i %% 8 + 1, n[i] + 1] + 1]
            }
            return(paste(x, inv[c + 1], sep = ""))
        }
    })

    return(y)
}

#' @rdname verhoeff
VerifyCheckDigit.Verhoeff <- function(x) {

    x[is.na(x)] <- NA

    if (any(grepl("\\D", x))) {
        warning("Non-digit characters are disregarded in check digit calculation")
    }

    d <- matrix(
        c(
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
            1, 2, 3, 4, 0, 6, 7, 8, 9, 5,
            2, 3, 4, 0, 1, 7, 8, 9, 5, 6,
            3, 4, 0, 1, 2, 8, 9, 5, 6, 7,
            4, 0, 1, 2, 3, 9, 5, 6, 7, 8,
            5, 9, 8, 7, 6, 0, 4, 3, 2, 1,
            6, 5, 9, 8, 7, 1, 0, 4, 3, 2,
            7, 6, 5, 9, 8, 2, 1, 0, 4, 3,
            8, 7, 6, 5, 9, 3, 2, 1, 0, 4,
            9, 8, 7, 6, 5, 4, 3, 2, 1, 0
        ),
        ncol = 10,
        byrow = TRUE
    )

    p <- matrix(
        c(
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
            1, 5, 7, 6, 2, 8, 3, 0, 9, 4,
            5, 8, 0, 3, 7, 9, 6, 1, 4, 2,
            8, 9, 1, 6, 0, 4, 3, 5, 2, 7,
            9, 4, 5, 3, 1, 2, 6, 8, 7, 0,
            4, 2, 8, 6, 5, 7, 3, 9, 0, 1,
            2, 7, 9, 3, 8, 0, 6, 4, 1, 5,
            7, 0, 4, 6, 9, 1, 3, 2, 5, 8
        ),
        ncol = 10,
        byrow = TRUE
    )

    y <- sapply(x, function(x) {
        y <- gsub("\\D", "", x)
        if (y %in% c("", NA, NaN)) {
            return(FALSE)
        } else {
            c <- 0
            n <- as.integer(rev(unlist(strsplit(y, ""))))
            for (i in seq(n)) {
                c <- d[c + 1, p[(i - 1) %% 8 + 1, n[i] + 1] + 1]
            }
            return(c == 0)
        }
    })

    return(y)
}

Try the CheckDigit package in your browser

Any scripts or data that you put into this service are public.

CheckDigit documentation built on April 24, 2022, 5:05 p.m.