Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Valid NPI Creation: "Double-Add-Double" Luhn #17

Closed
andrewallenbruce opened this issue Oct 12, 2023 · 2 comments
Closed

Valid NPI Creation: "Double-Add-Double" Luhn #17

andrewallenbruce opened this issue Oct 12, 2023 · 2 comments
Assignees
Labels
feature 📸 feature request validation 🧪 validation checks
Milestone

Comments

@andrewallenbruce
Copy link
Owner

Luhn Formula for Modulus 10 “double-add-double” Check Digit

Example of Check Digit Calculation for NPI used without Prefix:

# Assume the NPI is 1234567893:
npi_test <- "1234567893"
#> [1] "1234567893"

# Remove the 10th digit to create the 9-position identifier part of the NPI:
id <- unlist(strsplit(npi_test, ""))[1:9]
#> [1] "1" "2" "3" "4" "5" "6" "7" "8" "9"

# Reverse order of digits
x <- rev(id)
#> [1] "9" "8" "7" "6" "5" "4" "3" "2" "1"

# Select every other digit beginning with the leftmost digit:
idx <- seq(1, length(x), 2)
#> [1] 1 3 5 7 9

# Double the value of the alternate digits
x[idx] <- as.numeric(x[idx]) * 2
#> [1] "18" "8"  "14" "6"  "10" "4"  "6"  "2"  "2"

# Reverse order of digits again
x <- rev(x)
#> [1] "2"  "2"  "6"  "4"  "10" "6"  "14" "8"  "18"

# Split and unlist to separate digits
x <- unlist(strsplit(x, ""))
#>  [1] "2" "2" "6" "4" "1" "0" "6" "1" "4" "8" "1" "8"

# Add constant 24 to the sum of the digits
x <- sum(as.numeric(x)) + 24
#> [1] 67

# Find the next higher number ending in zero
y <- ceiling(x / 10) * 10
#> [1] 70

# Find the check digit by subtracting x from y
z <- y - x
#> [1] 3

# Append the check digit to the end of the 9-digit identifier
id[10] <- z
#>  [1] "1" "2" "3" "4" "5" "6" "7" "8" "9" "3"

# Collapse the vector into a single string
npi_valid <- paste0(id, collapse = "")
#> [1] "1234567893"

# The syntactically valid NPI is identical to the original NPI input
identical(npi_valid, npi_test)
#> [1] TRUE

Created on 2023-10-12 with reprex v2.0.2

@andrewallenbruce andrewallenbruce added the feature 📸 feature request label Oct 12, 2023
@andrewallenbruce andrewallenbruce added this to the 0.0.1 milestone Oct 12, 2023
@andrewallenbruce andrewallenbruce self-assigned this Oct 12, 2023
@andrewallenbruce andrewallenbruce added the validation 🧪 validation checks label Oct 24, 2023
@andrewallenbruce
Copy link
Owner Author

andrewallenbruce commented Oct 24, 2023

Could create an NPI constructor, ala:

Function code

library(provider)
library(dplyr)
library(purrr)
library(rlang)

create_npi <- function(npi,
                       arg = rlang::caller_arg(npi),
                       call = rlang::caller_env()) {
  
  # Must be numeric
  if (grepl("^[[:digit:]]+$", npi) == FALSE) {
    cli::cli_abort(c(
      "An {.strong NPI} must be {.emph numeric}.",
      "x" = "{.val {npi}} contains {.emph non-numeric} characters."),
      call = call)
  }
  
  # Must be 10 char length
  if (nchar(npi) != 10L) {
    cli::cli_abort(c(
      "An {.strong NPI} must be {.emph 100 digits long}.",
      "x" = "{.val {npi}} contains {.val {nchar(npi)}} digit{?s}."),
      call = call)
  }
  
  # Must pass Luhn algorithm
  npi_test <- as.character(npi)
  
  # Remove the 10th digit to create the 9-position identifier part of the NPI
  id <- unlist(strsplit(npi_test, ""), use.names = FALSE)[1:9]
  
  # Reverse order of digits
  x <- rev(id)
  
  # Select index of every other digit
  idx <- seq(1, length(x), 2)
  
  # Double the value of the alternate digits
  x[idx] <- as.numeric(x[idx]) * 2
  
  # Reverse order of digits again
  x <- rev(x)
  
  # Split and unlist to separate digits
  x <- unlist(strsplit(x, ""), use.names = FALSE)
  
  # Add constant 24 to the sum of the digits
  x <- sum(as.numeric(x)) + 24
  
  # Find the next higher number ending in zero
  y <- ceiling(x / 10) * 10
  
  # Find the check digit by subtracting x from y
  z <- y - x
  
  # Append the check digit to the end of the 9-digit identifier
  id[10] <- z
  
  # Collapse the vector into a single string
  npi_valid <- paste0(id, collapse = "")
  
  results <- list(
    input = npi_test,
    valid = npi_valid,
    equal = identical(npi_valid, npi_test))
  
  return(results)
}

# invalid
create_npi(1234567891)
#> $input
#> [1] "1234567891"
#> 
#> $valid
#> [1] "1234567893"
#> 
#> $equal
#> [1] FALSE

create_npi(1528060439)
#> $input
#> [1] "1528060439"
#> 
#> $valid
#> [1] "1528060431"
#> 
#> $equal
#> [1] FALSE

# valid
create_npi(1528060837)
#> $input
#> [1] "1528060837"
#> 
#> $valid
#> [1] "1528060837"
#> 
#> $equal
#> [1] TRUE

Created on 2023-10-23 with reprex v2.0.2

@andrewallenbruce andrewallenbruce changed the title NPI validation: Implement double-add-double Luhn Formula Valid NPI Creation: "Double-Add-Double" Luhn Oct 25, 2023
@andrewallenbruce
Copy link
Owner Author

andrewallenbruce commented Oct 27, 2023

Replace check_npi() calls with validate_npi()

  • affiliations()
  • clinicians()
  • hospitals()
  • nppes()
  • open_payments()
  • opt_out()
  • order_refer()
  • pending()
  • providers()
  • quality_payment()
  • quality_eligibility()
  • mips_2021()
  • reassignments()
  • utilization()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 📸 feature request validation 🧪 validation checks
Projects
None yet
Development

No branches or pull requests

1 participant