Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Algorithms Version History

**Changes in version 5.12**

- Fixed PRCDA algorithm returning 0 instead of 9 for unknown counties in states that now have PRCDA counties (like MD and TN); the states where every county is non-PRCDA are now computed from the PRCDA data.

**Changes in version 5.11**

- Updated data for the EPH SubCountry algorithms and switched to the 2020 geographies; the three 2010 output variables were retired and replaced by 2020 variables.
Expand Down
49 changes: 49 additions & 0 deletions src/main/java/com/imsweb/algorithms/internal/CountryData.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
Expand Down Expand Up @@ -128,6 +130,19 @@ else if (censusTract == CensusTract.CENSUS_2020)
// singleton instance
private static final CountryData _INSTANCE = new CountryData();

// the 50 states and DC; used to compute the states where every county is non-PRCDA
private static final List<String> _PRCDA_US_STATES = Arrays.asList("AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "DC", "FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY",
"LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT",
"VA", "WA", "WV", "WI", "WY");

// states where every county is PRCDA
private static final List<String> _PRCDA_ENTIRE_STATE_PRCDA = Arrays.asList("AK", "CT", "NV", "OK", "SC");

// territories, Canadian provinces and military codes; those are always non-PRCDA
private static final List<String> _PRCDA_TERRITORIES = Arrays.asList("AS", "GU", "MP", "PW", "PR", "UM", "VI", "FM", "MH", "TT");
private static final List<String> _PRCDA_PROVINCES = Arrays.asList("AB", "BC", "MB", "NB", "NL", "NS", "NT", "NU", "ON", "PE", "QC", "SK", "YT");
private static final List<String> _PRCDA_MILITARY = Arrays.asList("AA", "AE", "AP");

// unique access to the singleton
public static CountryData getInstance() {
return _INSTANCE;
Expand All @@ -148,6 +163,9 @@ public static CountryData getInstance() {
private boolean _prcdaInitialized = false;
private boolean _uihoInitialized = false;

// the states where every county is non-PRCDA (computed when the PRCDA data is initialized)
private final Set<String> _prcdaEntireStateNonPrcda = new HashSet<>();

// internal lock to control concurrency
private final ReentrantReadWriteLock _lock = new ReentrantReadWriteLock();

Expand All @@ -163,6 +181,7 @@ public void uninitializeAllData() {
_continuumStateInitialized.clear();
_countyAtDxAnalysisInitialized = false;
_prcdaInitialized = false;
_prcdaEntireStateNonPrcda.clear();
_uihoInitialized = false;
}
finally {
Expand Down Expand Up @@ -494,6 +513,28 @@ public StateData getPrcdaData(String state) {
}
}

/**
* Returns true if every county of the requested state is PRCDA, false otherwise.
*/
public boolean isPrcdaEntireStatePrcda(String state) {
return _PRCDA_ENTIRE_STATE_PRCDA.contains(state);
}

/**
* Returns true if every county of the requested state is non-PRCDA, false otherwise.
*/
public boolean isPrcdaEntireStateNonPrcda(String state) {
_lock.readLock().lock();
try {
if (!_prcdaInitialized)
throw new IllegalStateException("PRCDA data cannot be access before it has been initialized!");
return _prcdaEntireStateNonPrcda.contains(state);
}
finally {
_lock.readLock().unlock();
}
}

/**
* Returns true if the PRCDA data has been initialized, false otherwise.
*/
Expand Down Expand Up @@ -521,6 +562,14 @@ public void initializePrcdaData(Map<String, Map<String, CountyData>> data) {
countyData.setPrcda(countyEntry.getValue().getPrcda());
}
}

// every state that doesn't have any county in the PRCDA data (and that isn't entirely PRCDA) is entirely non-PRCDA
_PRCDA_US_STATES.stream()
.filter(state -> !data.containsKey(state) && !_PRCDA_ENTIRE_STATE_PRCDA.contains(state))
.forEach(_prcdaEntireStateNonPrcda::add);
_prcdaEntireStateNonPrcda.addAll(_PRCDA_TERRITORIES);
_prcdaEntireStateNonPrcda.addAll(_PRCDA_PROVINCES);
_prcdaEntireStateNonPrcda.addAll(_PRCDA_MILITARY);
}
_prcdaInitialized = true;
}
Expand Down
26 changes: 19 additions & 7 deletions src/main/java/com/imsweb/algorithms/prcda/PrcdaDataProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@

import static com.imsweb.algorithms.StateCountyInputDto.isInvalidStateOrCounty;
import static com.imsweb.algorithms.StateCountyInputDto.isUnknownStateOrCounty;
import static com.imsweb.algorithms.prcda.PrcdaUtils.ENTIRE_STATE_NON_PRCDA;
import static com.imsweb.algorithms.prcda.PrcdaUtils.ENTIRE_STATE_PRCDA;
import static com.imsweb.algorithms.prcda.PrcdaUtils.PRCDA_NO;
import static com.imsweb.algorithms.prcda.PrcdaUtils.PRCDA_UNKNOWN;
import static com.imsweb.algorithms.prcda.PrcdaUtils.PRCDA_YES;
Expand All @@ -31,16 +29,13 @@
public class PrcdaDataProvider {

public String getPrcda(String state, String county) {
if (ENTIRE_STATE_PRCDA.contains(state))
if (isEntireStatePrcda(state))
return PRCDA_YES;
else if (ENTIRE_STATE_NON_PRCDA.contains(state))
else if (isEntireStateNonPrcda(state))
return PRCDA_NO;
else if (isInvalidStateOrCounty(state, county) || isUnknownStateOrCounty(state, county) || "000".equals(county))
return PRCDA_UNKNOWN;

if (!CountryData.getInstance().isPrcdaDataInitialized())
CountryData.getInstance().initializePrcdaData(loadPrcdaData());

StateData stateData = CountryData.getInstance().getPrcdaData(state);
if (stateData == null)
return PRCDA_NO;
Expand All @@ -52,6 +47,23 @@ else if (isInvalidStateOrCounty(state, county) || isUnknownStateOrCounty(state,
return countyData.getPrcda();
}

/**
* Returns true if every county of the requested state is PRCDA, false otherwise.
*/
public boolean isEntireStatePrcda(String state) {
return CountryData.getInstance().isPrcdaEntireStatePrcda(state);
}

/**
* Returns true if every county of the requested state is non-PRCDA (based on the states present in the PRCDA data), false otherwise.
*/
public boolean isEntireStateNonPrcda(String state) {
if (!CountryData.getInstance().isPrcdaDataInitialized())
CountryData.getInstance().initializePrcdaData(loadPrcdaData());

return CountryData.getInstance().isPrcdaEntireStateNonPrcda(state);
}

private Map<String, Map<String, CountyData>> loadPrcdaData() {
Map<String, Map<String, CountyData>> result = new HashMap<>();

Expand Down
28 changes: 2 additions & 26 deletions src/main/java/com/imsweb/algorithms/prcda/PrcdaUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@
*/
package com.imsweb.algorithms.prcda;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.imsweb.algorithms.StateCountyInputDto;

/**
Expand All @@ -29,23 +22,6 @@ public final class PrcdaUtils {
public static final String PRCDA_YES = "1";
public static final String PRCDA_UNKNOWN = "9";

// States where every county is PRCDA
public static final Set<String> ENTIRE_STATE_PRCDA = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("AK", "CT", "NV", "OK", "SC")));

// States where every county is non-PRCDA
public static final Set<String> ENTIRE_STATE_NON_PRCDA;
static {
List<String> nonPrcda = new ArrayList<>(Arrays.asList("AR", "DE", "DC", "GA", "HI", "IL", "KY", "MD", "MO", "NH", "NJ", "OH", "TN", "VT", "WV"));
List<String> territory = Arrays.asList("AS", "GU", "MP", "PW", "PR", "UM", "VI", "FM", "MH", "TT");
List<String> province = Arrays.asList("AB", "BC", "MB", "NB", "NL", "NS", "NT", "NU", "ON", "PE", "QC", "SK", "YT");
List<String> military = Arrays.asList("AA", "AE", "AP");
//List<String> UNKNOWN_VALUES_DO_NOT_INCLUDE_IN_NON_PRCDA = Arrays.asList("CD", "US", "XX", "YY", "ZZ");
nonPrcda.addAll(territory);
nonPrcda.addAll(province);
nonPrcda.addAll(military);
ENTIRE_STATE_NON_PRCDA = Collections.unmodifiableSet(new HashSet<>(nonPrcda));
}

private static final PrcdaDataProvider _PROVIDER = new PrcdaDataProvider();

private PrcdaUtils() {
Expand Down Expand Up @@ -75,10 +51,10 @@ public static PrcdaOutputDto computePrcda(StateCountyInputDto input) {

input.applyRecodes();

if (ENTIRE_STATE_PRCDA.contains(input.getAddressAtDxState())) {
if (_PROVIDER.isEntireStatePrcda(input.getAddressAtDxState())) {
result.setPrcda(PRCDA_YES);
}
else if (ENTIRE_STATE_NON_PRCDA.contains(input.getAddressAtDxState())) {
else if (_PROVIDER.isEntireStateNonPrcda(input.getAddressAtDxState())) {
result.setPrcda(PRCDA_NO);
}
else {
Expand Down
14 changes: 14 additions & 0 deletions src/test/java/com/imsweb/algorithms/prcda/PrcdaUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ public void testComputePrcda() {
Assert.assertEquals(key, "0", PrcdaUtils.computePrcda(input).getPrcda());
}

// states that have some PRCDA counties shouldn't be considered entirely non-PRCDA, so unknown counties should return 9
for (String state : Arrays.asList("MD", "TN")) {
input.setAddressAtDxState(state);
input.setCountyAtDxAnalysis("999");
Assert.assertEquals(state, "9", PrcdaUtils.computePrcda(input).getPrcda());
}

// states that are not in the PRCDA data are entirely non-PRCDA
for (String state : Arrays.asList("AR", "DC", "WV", "ON", "AA")) {
input.setAddressAtDxState(state);
input.setCountyAtDxAnalysis("999");
Assert.assertEquals(state, "0", PrcdaUtils.computePrcda(input).getPrcda());
}

// test missing or invalid state or county - or county not reported
for (String state : Arrays.asList("MN", "ZZ", "INVALID", null)) {
for (String county : Arrays.asList("035", "000", "999", "INVALID", null)) {
Expand Down
Loading