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

Feature: NPI Constructor #62

Open
andrewallenbruce opened this issue Nov 23, 2023 · 2 comments
Open

Feature: NPI Constructor #62

andrewallenbruce opened this issue Nov 23, 2023 · 2 comments
Assignees
Milestone

Comments

@andrewallenbruce
Copy link
Owner

andrewallenbruce commented Nov 23, 2023

NPI Constructor

Function

library(provider)

construct_npi <- function(npi) {
  
  npi_test <- provider:::validate_npi(npi, print = TRUE)
  
  # Remove the 10th digit to create the 9-position identifier part of the NPI
  id <- unlist(strsplit(npi_test, ""), use.names = FALSE)[1:9]
  
  #---
  id_print <- rlang::sym(paste0(c(id, "_"), collapse = ""))
  cli::cli_alert("01. Keep first 9 digits: {.strong {.val {id_print}}}")
  #---
  
  # Reverse order of digits
  x <- rev(id)
  
  #---
  rev_print <- rlang::sym(paste0(x, collapse = ""))
  cli::cli_alert("02. Reverse order of digits:  {.strong {.val {rev_print}}}")
  #---
  
  # Select index of every other digit
  idx <- seq(1, length(x), 2)
  
  #---
  idx_print <- rlang::sym(paste0(x[idx], collapse = " "))
  cli::cli_alert("03. Select every other digit: {.strong {.val {idx_print}}}")
  #---
  
  # Double the value of the alternate digits
  x[idx] <- as.numeric(x[idx]) * 2
  
  #---
  dbl_print <- rlang::sym(paste0(x[idx], collapse = " "))
  cli::cli_alert("04. Double each alternate digit: {.strong {.val {dbl_print}}}")
  ins_print <- rlang::sym(paste0(x, collapse = " "))
  cli::cli_alert("05. Insert alternates back into original string: {.strong {.val {ins_print}}}")
  #---
  
  # Reverse order of digits again
  x <- rev(x)
  
  #---
  rev2 <- x
  rev2_print <- rlang::sym(paste0(x, collapse = " "))
  cli::cli_alert("06. Reverse order again: {.strong {.val {rev2_print}}}")
  #---
  
  
  # Split and unlist to separate digits
  x <- unlist(strsplit(x, ""), use.names = FALSE)
  
  #---
  rev2 <- unlist(strsplit(rev2, ""), use.names = FALSE)
  add <- sum(as.numeric(x))
  add_print <- rlang::sym(paste0(paste0(rev2, collapse = " + "), paste0(" = ", add)))
  cli::cli_alert("07. Sum the individual digits: {.strong {.val {add_print}}}")
  #---
  
  # Add constant 24 to the sum of the digits
  x <- sum(as.numeric(x)) + 24
  
  #---
  const_print <- rlang::sym(paste0(paste0(c(add, 24), collapse = " + "), paste0(" = ", add + 24)))
  cli::cli_alert("08. Add {.emph constant} {.strong 24} to the sum: {.strong {.val {const_print}}}")
  #---
  
  # Find the next higher number ending in zero
  y <- ceiling(x / 10) * 10
  
  #---
  int_print <- rlang::sym(paste0(paste0("", add + 24,""), paste0(" = ", y)))
  cli::cli_alert("09. Find next largest integer divisible by 10: {.strong {.val {int_print}}}")
  #---
  
  # Find the check digit by subtracting x from y
  z <- y - x
  
  #---
  check_print <- rlang::sym(paste0(paste0(c(y, (add + 24)), collapse = " - "), paste0(" = ", z)))
  cli::cli_alert(c("10. Find the check digit by subtracting {.emph step 08} from {.emph step 09}: 
{.strong {.val {check_print}}}"))
  #---
  
  # Append the check digit to the end of the 9-digit identifier
  id[10] <- z
  
  #---
  append_print <- rlang::sym(paste0(id, collapse = ""))
  cli::cli_alert(c("11. Append the check digit to the number from step 01: {.strong {.val {append_print}}}"))
  #---
  
  # Collapse the vector into a single string
  npi_valid <- paste0(id, collapse = "")
  
  # Is the syntactically valid NPI identical to the test NPI?
  if (identical(npi_valid, npi_test)) {
    
    #---
    identical <- rlang::sym(paste0(c(npi_test, npi_valid), collapse = " = "))
    cli::cli_alert_success(c("NPI {.strong is} syntactically valid: {.strong {.val {identical}}}"))
    #---
  }
  
  if (!identical(npi_valid, npi_test)) {
    
    #---
    not_identical <- rlang::sym(paste0(c(npi_test, npi_valid), collapse = " != "))
    cli::cli_alert_danger(c("NPI {.strong is not} syntactically valid: {.strong {.val {not_identical}}}"))
    #---
  }
}

Examples:

construct_npi(1234567891)
→ 01. Keep first 9 digits: 123456789_02. Reverse order of digits:  98765432103. Select every other digit: 9 7 5 3 104. Double each alternate digit: 18 14 10 6 205. Insert alternates back into original string: 18 8 14 6 10 4 6 2 206. Reverse order again: 2 2 6 4 10 6 14 8 1807. Sum the individual digits: 2 + 2 + 6 + 4 + 1 + 0 + 6 + 1 + 4 + 8 + 1 + 8 = 4308. Add constant 24 to the sum: 43 + 24 = 6709. Find next largest integer divisible by 10:67= 7010. Find the check digit by subtracting step 08 from step 09: 70 - 67 = 311. Append the check digit to the number from step 01: 1234567893NPI is not syntactically valid: 1234567891 != 1234567893

construct_npi(1043477615)
→ 01. Keep first 9 digits: 104347761_02. Reverse order of digits:  16774340103. Select every other digit: 1 7 4 4 104. Double each alternate digit: 2 14 8 8 205. Insert alternates back into original string: 2 6 14 7 8 3 8 0 206. Reverse order again: 2 0 8 3 8 7 14 6 207. Sum the individual digits: 2 + 0 + 8 + 3 + 8 + 7 + 1 + 4 + 6 + 2 = 4108. Add constant 24 to the sum: 41 + 24 = 6509. Find next largest integer divisible by 10:65= 7010. Find the check digit by subtracting step 08 from step 09: 70 - 65 = 511. Append the check digit to the number from step 01: 1043477615NPI is syntactically valid: 1043477615 = 1043477615

construct_npi("0000000000")
→ 01. Keep first 9 digits: 000000000_02. Reverse order of digits:  00000000003. Select every other digit: 0 0 0 0 004. Double each alternate digit: 0 0 0 0 005. Insert alternates back into original string: 0 0 0 0 0 0 0 0 006. Reverse order again: 0 0 0 0 0 0 0 0 007. Sum the individual digits: 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 = 008. Add constant 24 to the sum: 0 + 24 = 2409. Find next largest integer divisible by 10:24= 3010. Find the check digit by subtracting step 08 from step 09: 30 - 24 = 611. Append the check digit to the number from step 01: 0000000006NPI is not syntactically valid: 0000000000 != 0000000006

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

@andrewallenbruce
Copy link
Owner Author

Function

construct_npi <- function(npi) {
  
  npi_test <- provider:::validate_npi(npi, print = TRUE)
  
  #---
  cli_npi <- rlang::sym(npi_test)
  cli::cli_alert("Testing NPI: {.strong {.val {cli_npi}}}")
  #---
  
  # Remove the 10th digit to create the 9-position identifier part of the NPI
  id_9 <- unlist(strsplit(npi_test, ""), use.names = FALSE)[1:9]
  
  #---
  cli_id_9 <- rlang::sym(paste0(c(id_9, "_"), collapse = ""))
  cli::cli_alert("Keep first 9 digits: {.strong {.val {cli_id_9}}}")
  #---
  
  # Reverse order of digits
  x_rev <- rev(id_9)
  
  #---
  cli_x_rev <- rlang::sym(paste0(x_rev, collapse = ""))
  cli::cli_alert("Reverse digit order:  {.strong {.val {cli_x_rev}}}")
  #---
  
  # Select index of every other digit
  x_idx <- seq(1, length(x_rev), 2)
  
  #---
  cli_x_idx <- rlang::sym(paste0(x_rev[x_idx], collapse = " "))
  cli::cli_alert("Select every other digit: {.strong {.val {cli_x_idx}}}")
  #---
  
  # Double the value of the alternate digits
  x_rev[x_idx] <- as.numeric(x_rev[x_idx]) * 2
  
  #---
  cli_dbl <- rlang::sym(paste0(x_rev[x_idx], collapse = " "))
  cli_ins <- rlang::sym(paste0(x_rev, collapse = " "))
  cli::cli_alert("Double each: {.strong {.val {cli_dbl}}}")
  cli::cli_alert("Replace original with doubles: {.strong {.val {cli_ins}}}")
  #---
  
  # Split and unlist to separate digits
  x_split <- unlist(strsplit(x_rev, ""), use.names = FALSE)
  
  x_add <- sum(as.numeric(x_split))

  #---
  cli_x_add <- rlang::sym(paste0(paste0(x_split, collapse = " + "), paste0(" = ", x_add)))
  cli::cli_alert("Sum individual digits: {.strong {.val {cli_x_add}}}")
  #---
  
  # Add constant 24 to the sum of the digits
  x_24 <- x_add + 24
  
  #---
  sym_24 <- rlang::sym("24")
  cli_x_24 <- rlang::sym(paste0(paste0(c(x_add, 24), collapse = " + "), paste0(" = ", x_24)))
  cli::cli_alert("Add {.strong {.val {sym_24}}} to the sum: {.strong {.val {cli_x_24}}}")
  #---

  x_divide <- x_24 / 10
  cli_x_divide <- rlang::sym(paste0(paste0(x_24, " / ", 10), paste0(" = ", x_divide)))
  cli::cli_alert("Divide by 10: {.strong {.val {cli_x_divide}}}")
  
  x_divide_first <- as.numeric(unlist(strsplit(as.character(x_divide), ""), use.names = FALSE)[1])
  cli_x_first <- rlang::sym(paste0(paste0(x_divide), paste0(" = ", x_divide_first)))
  cli::cli_alert("Take first digit: {.strong {.val {cli_x_first}}}")
  
  x_plus_one <- x_divide_first + 1
  cli_x_plus <- rlang::sym(paste0(paste0(x_divide_first, " + ", 1), paste0(" = ", x_plus_one)))
  cli::cli_alert("Add one: {.strong {.val {cli_x_plus}}}")
  
  x_paste_zero <- paste0(x_plus_one, "0")
  cli_x_zero <- rlang::sym(paste0(paste0(x_plus_one, " * ", 10), paste0(" = ", x_paste_zero)))
  cli::cli_alert("Multiply by 10: {.strong {.val {cli_x_zero}}}")
  
  
  # Find the next higher number ending in zero
  x_next <- ceiling(x_24 / 10) * 10
  
  #---
  cli_x_next <- rlang::sym(paste0(paste0("", x_24,""), paste0(" = " , x_next)))
  cli::cli_alert("This is the next largest integer divisible by 10: {.strong {.val {cli_x_next}}}")
  #---
  
  # Find the check digit by subtracting x from y
  x_check <- x_next - x_24
  
  #---
  cli_x_check <- rlang::sym(paste0(paste0(c(x_next, x_24), collapse = " - "), paste0(" = ", x_check)))
  cli::cli_alert(c("Find the check digit: {.strong {.val {cli_x_check}}}"))
  #---
  
  # Append the check digit to the end of the 9-digit identifier
  id_10 <- id_9
  id_10[10] <- x_check
  
  #---
  cli_id_10 <- rlang::sym(paste0(id_10, collapse = ""))
  cli::cli_alert(c("Append check digit to the original value: {.strong {.val {cli_id_10}}}"))
  #---
  
  # Collapse the vector into a single string
  npi_valid <- paste0(id_10, collapse = "")
  
  # Is the syntactically valid NPI identical to the test NPI?
  if (identical(npi_valid, npi_test)) {
    
    #---
    identical <- rlang::sym(paste0(c(npi_test, npi_valid), collapse = " = "))
    cli::cli_alert_success(c("NPI {.strong is} syntactically valid: {.strong {.val {identical}}}"))
    #---
  }
  
  if (!identical(npi_valid, npi_test)) {
    
    #---
    not_identical <- rlang::sym(paste0(c(npi_test, npi_valid), collapse = " != "))
    cli::cli_alert_danger(c("NPI {.strong is not} syntactically valid: {.strong {.val {not_identical}}}"))
    #---
  }
}

construct_npi(1234567891)

→ Testing NPI: 1234567891Keep first 9 digits: 123456789_Reverse digit order:  987654321Select every other digit: 9 7 5 3 1Double each: 18 14 10 6 2Replace original with doubles: 18 8 14 6 10 4 6 2 2Sum individual digits: 1 + 8 + 8 + 1 + 4 + 6 + 1 + 0 + 4 + 6 + 2 + 2 = 43Add 24 to the sum: 43 + 24 = 67Divide by 10: 67 / 10 = 6.7Take first digit: 6.7 = 6Add one: 6 + 1 = 7Multiply by 10: 7 * 10 = 70This is the next largest integer divisible by 10:67= 70Find the check digit: 70 - 67 = 3Append check digit to the original value: 1234567893NPI is not syntactically valid: 1234567891 != 1234567893

construct_npi(1043477615)

→ Testing NPI: 1043477615Keep first 9 digits: 104347761_Reverse digit order:  167743401Select every other digit: 1 7 4 4 1Double each: 2 14 8 8 2Replace original with doubles: 2 6 14 7 8 3 8 0 2Sum individual digits: 2 + 6 + 1 + 4 + 7 + 8 + 3 + 8 + 0 + 2 = 41Add 24 to the sum: 41 + 24 = 65Divide by 10: 65 / 10 = 6.5Take first digit: 6.5 = 6Add one: 6 + 1 = 7Multiply by 10: 7 * 10 = 70This is the next largest integer divisible by 10:65= 70Find the check digit: 70 - 65 = 5Append check digit to the original value: 1043477615NPI is syntactically valid: 1043477615 = 1043477615

construct_npi("0000000000")

→ Testing NPI: 0000000000Keep first 9 digits: 000000000_Reverse digit order:  000000000Select every other digit: 0 0 0 0 0Double each: 0 0 0 0 0Replace original with doubles: 0 0 0 0 0 0 0 0 0Sum individual digits: 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 = 0Add 24 to the sum: 0 + 24 = 24Divide by 10: 24 / 10 = 2.4Take first digit: 2.4 = 2Add one: 2 + 1 = 3Multiply by 10: 3 * 10 = 30This is the next largest integer divisible by 10:24= 30Find the check digit: 30 - 24 = 6Append check digit to the original value: 0000000006NPI is not syntactically valid: 0000000000 != 0000000006

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

@andrewallenbruce
Copy link
Owner Author

andrewallenbruce commented Jul 31, 2024

luhn <- function(x) {
  
  x <- rev(as.integer(unlist(strsplit(x, ""))))
  
  idx_odd  <- seq_along(x) %% 2 == 1
  idx_even <- seq_along(x) %% 2 == 0
  
  x[idx_even] <- x[idx_even] * 2
  x[idx_even] <- ifelse(x[idx_even] > 9, x[idx_even] - 9, x[idx_even])
  
  sum_odd  <- sum(x[idx_odd])
  sum_even <- sum(x[idx_even])
  
  sum_x <- sum_odd + sum_even
  
  sum_x %% 10 == 0
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant