VaccinationConfigGroup.java
package org.matsim.episim;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import org.matsim.core.config.ConfigGroup;
import org.matsim.core.config.ReflectiveConfigGroup;
import org.matsim.episim.model.VaccinationType;
import org.matsim.episim.model.VirusStrain;
import org.matsim.episim.model.vaccination.VaccinationModel;
import java.time.LocalDate;
import java.util.EnumMap;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
/**
* Config option specific to vaccination and measures performed in {@link VaccinationModel}.
*/
public class VaccinationConfigGroup extends ReflectiveConfigGroup {
private static final Splitter.MapSplitter SPLITTER = Splitter.on(";").withKeyValueSeparator("=");
private static final Joiner.MapJoiner JOINER = Joiner.on(";").withKeyValueSeparator("=");
private static final String COMPLIANCE = "compliance";
private static final String CAPACITY = "vaccinationCapacity";
private static final String RECAPACITY = "reVaccinationCapacity";
private static final String SHARE = "vaccinationShare";
private static final String FROM_FILE = "vaccinationFile";
private static final String DAYS_VALID = "daysValid";
private static final String BETA = "beta";
private static final String IGA = "IGA";
private static final String TIME_PERIOD_IGA = "timePeriodIgA";
private static final String VALID_DEADLINE = "validDeadline";
private static final String GROUPNAME = "episimVaccination";
/**
* Amount of vaccinations available per day.
*/
private final NavigableMap<LocalDate, Integer> vaccinationCapacity = new TreeMap<>();
/**
* Amount of re-vaccinations available per day.
*/
private final NavigableMap<LocalDate, Integer> reVaccinationCapacity = new TreeMap<>();
/**
* Share of vaccination for the different {@link VaccinationType}.
*/
private final NavigableMap<LocalDate, Map<VaccinationType, Double>> vaccinationShare = new TreeMap<>(Map.of(LocalDate.EPOCH, Map.of(VaccinationType.generic, 1d)));
/**
* Load vaccinations from file instead.
*/
private String fromFile;
/**
* Validity of vaccination in days.
*/
private int daysValid = 180;
/**
* Needed for antibody model.
*/
private double beta = 1.0;
/**
* Needed for antibody model.
*/
private boolean useIgA = false;
private double timePeriodIgA = 120.;
/**
* Deadline after which days valid is in effect.
*/
private LocalDate validDeadline = LocalDate.of(2022, 2, 1);
/**
* Vaccination compliance by age groups. Keys are the left bounds of age group intervals.
* -1 is used as lookup when no age is present.
*/
private final NavigableMap<Integer, Double> compliance = new TreeMap<>(Map.of(-1, 1.0));
/**
* Holds all specific vaccination params.
*/
private final Map<VaccinationType, VaccinationParams> params = new EnumMap<>(VaccinationType.class);
/**
* Default constructor.
*/
public VaccinationConfigGroup() {
super(GROUPNAME);
// add default params
getOrAddParams(VaccinationType.generic);
}
/**
* Get config parameter for a specific vaccination type.
*/
public VaccinationParams getParams(VaccinationType type) {
if (!params.containsKey(type))
throw new IllegalStateException("Vaccination type " + type + " is not configured.");
return params.get(type);
}
/**
* Whether config contains certain type.
*/
public boolean hasParams(VaccinationType type) {
return params.containsKey(type);
}
/**
* Get an existing or add new parameter set.
*/
public VaccinationParams getOrAddParams(VaccinationType type) {
if (!params.containsKey(type)) {
VaccinationParams p = new VaccinationParams();
p.type = type;
addParameterSet(p);
return p;
}
return params.get(type);
}
@Override
public ConfigGroup createParameterSet(String type) {
if (VaccinationParams.SET_TYPE.equals(type)) {
return new VaccinationParams();
}
throw new IllegalArgumentException("Unknown type " + type);
}
@Override
public void addParameterSet(final ConfigGroup set) {
if (VaccinationParams.SET_TYPE.equals(set.getName())) {
VaccinationParams p = (VaccinationParams) set;
params.put(p.type, p);
super.addParameterSet(set);
} else
throw new IllegalStateException("Unknown set type " + set.getName());
}
/**
* Set vaccination compliance by age.
*
* @see #compliance
*/
public void setCompliancePerAge(Map<Integer, Double> compliance) {
this.compliance.clear();
this.compliance.putAll(compliance);
}
/**
* Get vaccination compliance by age.
*/
public NavigableMap<Integer, Double> getCompliancePerAge() {
return compliance;
}
@StringSetter(COMPLIANCE)
void setCompliance(String compliance) {
Map<String, String> map = SPLITTER.split(compliance);
setCompliancePerAge(map.entrySet().stream().collect(Collectors.toMap(
e -> Integer.parseInt(e.getKey()), e -> Double.parseDouble(e.getValue())
)));
}
@StringGetter(COMPLIANCE)
String getComplianceString() {
return JOINER.join(compliance);
}
/**
* Sets the vaccination capacity for individual days. If a day has no entry the previous will be still valid.
* If empty, default is 0.
*
* @param capacity map of dates to changes in capacity.
*/
public void setVaccinationCapacity_pers_per_day(Map<LocalDate, Integer> capacity) {
vaccinationCapacity.clear();
vaccinationCapacity.putAll(capacity);
}
public NavigableMap<LocalDate, Integer> getVaccinationCapacity() {
return vaccinationCapacity;
}
@StringSetter(CAPACITY)
void setVaccinationCapacity(String capacity) {
if (capacity.isBlank())
return;
Map<String, String> map = SPLITTER.split(capacity);
setVaccinationCapacity_pers_per_day(map.entrySet().stream().collect(Collectors.toMap(
e -> LocalDate.parse(e.getKey()), e -> Integer.parseInt(e.getValue())
)));
}
@StringGetter(CAPACITY)
String getVaccinationCapacityString() {
return JOINER.join(vaccinationCapacity);
}
@StringSetter(FROM_FILE)
public void setFromFile(String fromFile) {
this.fromFile = fromFile;
}
@StringGetter(FROM_FILE)
public String getFromFile() {
return fromFile;
}
@StringSetter(DAYS_VALID)
public void setDaysValid(int daysValid) {
this.daysValid = daysValid;
}
@StringGetter(DAYS_VALID)
int getDaysValid() {
return daysValid;
}
@StringSetter(BETA)
public void setBeta(double beta) {
this.beta = beta;
}
@StringGetter(BETA)
public double getBeta() {
return beta;
}
@StringSetter(IGA)
public void setUseIgA(boolean useIgA) {
this.useIgA = useIgA;
}
@StringGetter(IGA)
public boolean getUseIgA() {
return useIgA;
}
@StringSetter(TIME_PERIOD_IGA)
public void setTimePeriodIgA(double timePeriodIgA) {
this.timePeriodIgA = timePeriodIgA;
}
@StringGetter(TIME_PERIOD_IGA)
public double getTimePeriodIgA() {
return this.timePeriodIgA;
}
@StringSetter(VALID_DEADLINE)
public void setValidDeadline(String validDeadline) {
this.validDeadline = LocalDate.parse(validDeadline);
}
public void setValidDeadline(LocalDate validDeadline) {
this.validDeadline = validDeadline;
}
@StringGetter(VALID_DEADLINE)
public LocalDate getValidDeadline() {
return validDeadline;
}
/**
* Check if person is recently recovered or vaccinated.
*/
public boolean hasGreenPass(EpisimPerson person, int day, LocalDate date) {
return hasGreenPass(person, day, date, daysValid);
}
/**
* Check 2G plus status, but use given {@code daysValid}.
*/
public boolean hasGreenPass(EpisimPerson person, int day, LocalDate date, int daysValid) {
return hasRecoveredStatus(person, day, date, daysValid > -1 ? daysValid : this.daysValid) || hasValidVaccination(person, day, date, daysValid > -1 ? daysValid : this.daysValid);
}
/**
* Special type of green pass with separate setting for boostered or equivalent status.
*/
public boolean hasGreenPassForBooster(EpisimPerson p, int day, LocalDate date, int greenPassValidDays, int greenPassBoosterValidDays) {
int valid = greenPassValidDays;
// infected and vaccinated count as booster
if (p.getReVaccinationStatus() == EpisimPerson.VaccinationStatus.yes || (p.getNumInfections() >= 1 && p.getVaccinationStatus() == EpisimPerson.VaccinationStatus.yes))
valid = greenPassBoosterValidDays;
return hasGreenPass(p, day, date, valid);
}
/**
* Check whether person has the recovered status.
*/
private boolean hasRecoveredStatus(EpisimPerson person, int day, LocalDate date, int daysValid) {
// Initial the threshold was 180 days, this setting is adjusted to the threshold after the deadline
return date.isBefore(validDeadline) ? person.isRecentlyRecovered(day, 180) : person.isRecentlyRecovered(day, daysValid);
}
/**
* Check if person has a valid vaccination card.
*
* @param person person to check
* @param day current simulation day
* @param date simulation date
*/
public boolean hasValidVaccination(EpisimPerson person, int day, LocalDate date) {
return hasValidVaccination(person, day, date, getDaysValid());
}
public boolean hasValidVaccination(EpisimPerson person, int day, LocalDate date, int daysValid) {
if (person.getVaccinationStatus() == EpisimPerson.VaccinationStatus.no)
return false;
boolean fullyVaccinated = person.daysSince(EpisimPerson.VaccinationStatus.yes, day) > getParams(person.getVaccinationType()).getDaysBeforeFullEffect();
boolean booster = person.getReVaccinationStatus() == EpisimPerson.VaccinationStatus.yes;
if (date.isBefore(validDeadline))
return fullyVaccinated || booster;
return (fullyVaccinated || booster) && person.daysSince(EpisimPerson.VaccinationStatus.yes, day) <= daysValid;
}
/**
* Computes the minimum factor over all vaccinations.
* @param person person
* @param day current iteration
* @param f function of VaccinationParams to retrieve the desired factor
* @return minimum factor or 1 if not vaccinated
*/
public double getMinFactor(EpisimPerson person, int day, VaccinationFactorFunction f) {
if (person.getNumVaccinations() == 0)
return 1;
double factor = 1d;
for (int i = 0; i < person.getNumVaccinations(); i++) {
VaccinationType type = person.getVaccinationType(i);
factor = Math.min(factor, f.getFactor(getParams(type), person.getVirusStrain(), person.daysSince(EpisimPerson.VaccinationStatus.yes, day)));
}
return factor;
}
/**
* @see #setVaccinationCapacity_pers_per_day(Map)
*/
public void setReVaccinationCapacity_pers_per_day(Map<LocalDate, Integer> capacity) {
reVaccinationCapacity.clear();
reVaccinationCapacity.putAll(capacity);
}
public NavigableMap<LocalDate, Integer> getReVaccinationCapacity() {
return reVaccinationCapacity;
}
@StringSetter(RECAPACITY)
void setReVaccinationCapacity(String capacity) {
if (capacity.isBlank())
return;
Map<String, String> map = SPLITTER.split(capacity);
setReVaccinationCapacity_pers_per_day(map.entrySet().stream().collect(Collectors.toMap(
e -> LocalDate.parse(e.getKey()), e -> Integer.parseInt(e.getValue())
)));
}
@StringGetter(RECAPACITY)
String getReVaccinationCapacityString() {
return JOINER.join(reVaccinationCapacity);
}
/**
* Set the vaccination share per date.
*/
public void setVaccinationShare(Map<LocalDate, Map<VaccinationType, Double>> share) {
for (Map<VaccinationType, Double> v : share.values()) {
double total = v.values().stream().sorted().mapToDouble(Double::doubleValue).sum();
if (total > 1) throw new IllegalArgumentException("Sum of shares must be < 1");
}
this.vaccinationShare.clear();
this.vaccinationShare.putAll(share);
}
/**
* Return vaccination share per date.
*/
public NavigableMap<LocalDate, Map<VaccinationType, Double>> getVaccinationShare() {
return vaccinationShare;
}
/**
* Return the cumulative probability for all vaccination types, based on {@link #getVaccinationShare()}.
*
* @param date date to lookup
*/
public Map<VaccinationType, Double> getVaccinationTypeProb(LocalDate date) {
EnumMap<VaccinationType, Double> prob = new EnumMap<>(VaccinationType.class);
Map<VaccinationType, Double> share = EpisimUtils.findValidEntry(vaccinationShare, null, date);
if (share == null)
share = Map.of(VaccinationType.generic, 1d);
double total = share.values().stream().sorted().mapToDouble(Double::doubleValue).sum();
double sum = 1 - total;
for (VaccinationType t : VaccinationType.values()) {
sum += share.getOrDefault(t, 0d);
prob.put(t, sum);
}
return prob;
}
@StringGetter(SHARE)
String getVaccinationShareString() {
Map<LocalDate, String> collect =
vaccinationShare.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> JOINER.join(e.getValue())));
return Joiner.on("|").withKeyValueSeparator(">").join(collect);
}
@StringSetter(SHARE)
void setVaccinationShare(String value) {
Map<String, String> share = Splitter.on("|").withKeyValueSeparator(">").split(value);
Map<LocalDate, Map<VaccinationType, Double>> collect = share.entrySet().stream().collect(Collectors.toMap(
e -> LocalDate.parse(e.getKey()),
e -> SPLITTER.split(e.getValue()).entrySet().stream().collect(Collectors.toMap(k -> VaccinationType.valueOf(k.getKey()), k -> Double.parseDouble(k.getValue())))
));
setVaccinationShare(collect);
}
/**
* Holds strain specific options.
*/
public static final class VaccinationParams extends ReflectiveConfigGroup {
static final String SET_TYPE = "vaccinationParams";
private static final String TYPE = "type";
private static final String DAYS_BEFORE_FULL_EFFECT = "daysBeforeFullEffect";
private static final String EFFECTIVENESS = "effectiveness";
private static final String INFECTIVITY = "infectivity";
private static final String BOOST_EFFECTIVENESS = "boostEffectiveness";
private static final String BOOST_INFECTIVITY = "boostInfectivity";
private static final String BOOST_WAIT_PERIOD = "boostWaitPeriod";
private static final String FACTOR_SHOWINGS_SYMPTOMS = "factorShowingSymptoms";
private static final String FACTOR_SERIOUSLY_SICK = "factorSeriouslySick";
private static final String FACTOR_CRITICAL = "factorCritical";
private VaccinationType type;
/**
* Number of days until vaccination goes into full effect.
*/
private int daysBeforeFullEffect = 28;
/**
* Wait period before boost can be applied.
*/
private int boostWaitPeriod = 5 * 30;
/**
* Effectiveness, i.e. how much susceptibility is reduced.
*/
private Map<VirusStrain, Parameter> effectiveness = new EnumMap<>(Map.of(VirusStrain.SARS_CoV_2,
forStrain(VirusStrain.SARS_CoV_2)
.atDay(4, 0)
.atDay(5, 0.45)
.atFullEffect(0.9)
));
/**
* Infectivity of a vaccinated person towards others.
*/
private Map<VirusStrain, Parameter> infectivity = new EnumMap<>(Map.of(VirusStrain.SARS_CoV_2,
forStrain(VirusStrain.SARS_CoV_2)
.atDay(0, 1)
.atFullEffect(1.0)
));
/**
* Effectiveness after booster shot.
*/
private Map<VirusStrain, Parameter> boostEffectiveness = new EnumMap<>(VirusStrain.class);
/**
* Infectivity of a vaccinated person towards others.
*/
private Map<VirusStrain, Parameter> boostInfectivity = new EnumMap<>(Map.of(VirusStrain.SARS_CoV_2,
forStrain(VirusStrain.SARS_CoV_2)
.atDay(0, 1)
.atFullEffect(1.0)
));
/**
* Factor for probability if person is vaccinated.
*/
private Map<VirusStrain, Parameter> factorShowingSymptoms = new EnumMap<>(Map.of(VirusStrain.SARS_CoV_2,
forStrain(VirusStrain.SARS_CoV_2)
.atDay(5, 0.5)
));
/**
* Factor for probability if person is vaccinated.
*/
private Map<VirusStrain, Parameter> factorSeriouslySick = new EnumMap<>(Map.of(VirusStrain.SARS_CoV_2,
forStrain(VirusStrain.SARS_CoV_2)
.atDay(5, 0.5)
));
/**
* Factor for probability if person is vaccinated.
*/
private Map<VirusStrain, Parameter> factorCritical = new EnumMap<>(Map.of(VirusStrain.SARS_CoV_2,
forStrain(VirusStrain.SARS_CoV_2)
.atDay(0, 1)
));
VaccinationParams() {
super(SET_TYPE);
}
@StringGetter(TYPE)
public VaccinationType getType() {
return type;
}
@StringSetter(TYPE)
public void setType(VaccinationType type) {
this.type = type;
}
@StringGetter(DAYS_BEFORE_FULL_EFFECT)
public int getDaysBeforeFullEffect() {
return daysBeforeFullEffect;
}
@StringSetter(DAYS_BEFORE_FULL_EFFECT)
public VaccinationParams setDaysBeforeFullEffect(int daysBeforeFullEffect) {
this.daysBeforeFullEffect = daysBeforeFullEffect;
return this;
}
@StringSetter(BOOST_WAIT_PERIOD)
public VaccinationParams setBoostWaitPeriod(int boostWaitPeriod) {
this.boostWaitPeriod = boostWaitPeriod;
return this;
}
@StringGetter(BOOST_WAIT_PERIOD)
public int getBoostWaitPeriod() {
return boostWaitPeriod;
}
private VaccinationParams setParamsInternal(Map<VirusStrain, Parameter> map, Parameter[] params) {
for (Parameter p : params) {
for (VirusStrain s : p.strain) {
p.setDaysBeforeFullEffect(getDaysBeforeFullEffect());
map.put(s, p);
}
}
return this;
}
/**
* Interpolate parameter for day after vaccination.
*
* @param map map for lookup
* @param strain virus strain
* @param day days since vaccination
* @return interpolated factor
*/
private double getParamsInternal(Map<VirusStrain, Parameter> map, VirusStrain strain, int day) {
Parameter p = map.getOrDefault(strain, map.get(VirusStrain.SARS_CoV_2));
return p.get(day);
}
public VaccinationParams setEffectiveness(Parameter... parameters) {
return setParamsInternal(effectiveness, parameters);
}
public VaccinationParams setInfectivity(Parameter... parameters) {
return setParamsInternal(infectivity, parameters);
}
public VaccinationParams setBoostEffectiveness(Parameter... parameters) {
return setParamsInternal(boostEffectiveness, parameters);
}
public VaccinationParams setBoostInfectivity(Parameter... parameters) {
return setParamsInternal(boostInfectivity, parameters);
}
public VaccinationParams setFactorShowingSymptoms(Parameter... parameters) {
return setParamsInternal(factorShowingSymptoms, parameters);
}
public VaccinationParams setFactorSeriouslySick(Parameter... parameters) {
return setParamsInternal(factorSeriouslySick, parameters);
}
public VaccinationParams setFactorCritical(Parameter... parameters) {
return setParamsInternal(factorCritical, parameters);
}
public double getEffectiveness(VirusStrain strain, int day) {
return getParamsInternal(effectiveness, strain, day);
}
public double getInfectivity(VirusStrain strain, int day) {
return getParamsInternal(infectivity, strain, day);
}
public double getBoostInfectivity(VirusStrain strain, int day) {
return getParamsInternal(boostInfectivity, strain, day);
}
public double getBoostEffectiveness(VirusStrain strain, int day) {
return getParamsInternal(boostEffectiveness.containsKey(strain) ? boostEffectiveness : effectiveness, strain, day);
}
public double getFactorShowingSymptoms(VirusStrain strain, int day) {
return getParamsInternal(factorShowingSymptoms, strain, day);
}
public double getFactorSeriouslySick(VirusStrain strain, int day) {
return getParamsInternal(factorSeriouslySick, strain, day);
}
public double getFactorCritical(VirusStrain strain, int day) {
return getParamsInternal(factorCritical, strain, day);
}
/**
* Load serialized parameters
*/
private void setParamsInternal(Map<VirusStrain, Parameter> map, String value) {
map.clear();
if (value.isBlank()) return;
map.clear();
for (Map.Entry<String, String> e : SPLITTER.split(value).entrySet()) {
map.put(VirusStrain.valueOf(e.getKey()), Parameter.parse(e.getValue()));
}
}
private String getParamsInternal(Map<VirusStrain, Parameter> map) {
Map<VirusStrain, String> result = map.entrySet().stream().collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().toString()
));
return JOINER.join(result);
}
@StringSetter(EFFECTIVENESS)
void setEffectiveness(String value) {
setParamsInternal(effectiveness, value);
}
@StringGetter(EFFECTIVENESS)
String getEffectivenessString() {
return getParamsInternal(effectiveness);
}
@StringSetter(BOOST_EFFECTIVENESS)
void setBoostEffectiveness(String value) {
setParamsInternal(boostEffectiveness, value);
}
@StringGetter(BOOST_EFFECTIVENESS)
String getBoostEffectivenessString() {
return getParamsInternal(boostEffectiveness);
}
@StringSetter(FACTOR_SHOWINGS_SYMPTOMS)
void setFactorShowingSymptoms(String value) {
setParamsInternal(factorShowingSymptoms, value);
}
@StringGetter(FACTOR_SHOWINGS_SYMPTOMS)
String getFactorShowingSymptoms() {
return getParamsInternal(factorShowingSymptoms);
}
@StringSetter(FACTOR_SERIOUSLY_SICK)
void setFactorSeriouslySick(String value) {
setParamsInternal(factorSeriouslySick, value);
}
@StringGetter(FACTOR_SERIOUSLY_SICK)
String getFactorSeriouslySick() {
return getParamsInternal(factorSeriouslySick);
}
@StringSetter(FACTOR_CRITICAL)
void setFactorCritical(String value) {
setParamsInternal(factorCritical, value);
}
@StringGetter(FACTOR_CRITICAL)
public String getFactorCritical() {
return getParamsInternal(factorCritical);
}
@StringSetter(INFECTIVITY)
void setInfectivity(String value) {
setParamsInternal(infectivity, value);
}
@StringGetter(INFECTIVITY)
public String getInfectivity() {
return getParamsInternal(infectivity);
}
@StringSetter(BOOST_INFECTIVITY)
void setBoostInfectivity(String value) {
setParamsInternal(boostInfectivity, value);
}
@StringGetter(BOOST_INFECTIVITY)
public String getBoostInfectivity() {
return getParamsInternal(boostInfectivity);
}
/**
* Return effectiveness against base variant.
*
* @deprecated use {@link #getEffectiveness(VirusStrain, int)}
*/
@Deprecated
public double getEffectiveness() {
return getEffectiveness(VirusStrain.SARS_CoV_2, getDaysBeforeFullEffect());
}
/**
* Return effectiveness against base variant.
*
* @deprecated use {@link #setEffectiveness(Parameter...)}
*/
@Deprecated
public void setEffectiveness(double effectiveness) {
throw new UnsupportedOperationException("Use .setEffectiveness(Parameter...)");
}
@Deprecated
public VaccinationParams setFactorSeriouslySick(double factorSeriouslySick) {
throw new UnsupportedOperationException("Use .setFactorSeriouslySick(Parameter...)");
}
@Deprecated
public VaccinationParams setFactorShowingSymptoms(double factorShowingSymptoms) {
throw new UnsupportedOperationException("Use .setFactorShowingSymptoms(Parameter...)");
}
}
/**
* Creates an empty {@link Parameter} progression for one or multiple strain.
*/
public static Parameter forStrain(VirusStrain... strain) {
return new Parameter(strain);
}
/**
* Holds the temporal progression of certain value for each virus strains.
*/
public static final class Parameter {
private static final Splitter.MapSplitter SPLITTER = Splitter.on("|").withKeyValueSeparator(">");
private static final Joiner.MapJoiner JOINER = Joiner.on("|").withKeyValueSeparator(">");
private final VirusStrain[] strain;
private final NavigableMap<Integer, Double> map = new TreeMap<>();
private Parameter(VirusStrain[] strain) {
this.strain = strain;
}
private Parameter(Map<String, String> map) {
this.strain = new VirusStrain[0];
for (Map.Entry<String, String> e : map.entrySet()) {
this.map.put(Integer.parseInt(e.getKey()), Double.parseDouble(e.getValue()));
}
}
/**
* Sets the value for a parameter at a specific day.
*/
public Parameter atDay(int day, double value) {
map.put(day, value);
return this;
}
/**
* Sets the value for parameter for the day of full effect.
* {@link VaccinationParams#setDaysBeforeFullEffect(int)} has to be set before calling this method!
*/
public Parameter atFullEffect(double value) {
map.put(Integer.MAX_VALUE, value);
return this;
}
/**
* Interpolate for given day.
*/
private double get(int day) {
Map.Entry<Integer, Double> floor = map.floorEntry(day);
if (floor == null)
return map.firstEntry().getValue();
if (floor.getKey().equals(day))
return floor.getValue();
Map.Entry<Integer, Double> ceil = map.ceilingEntry(day);
// there is no higher entry to interpolate
if (ceil == null)
return floor.getValue();
double between = ceil.getKey() - floor.getKey();
double diff = day - floor.getKey();
return floor.getValue() + diff * (ceil.getValue() - floor.getValue()) / between;
}
private void setDaysBeforeFullEffect(int daysBeforeFullEffect) {
if (map.containsKey(Integer.MAX_VALUE))
map.put(daysBeforeFullEffect, map.remove(Integer.MAX_VALUE));
}
@Override
public String toString() {
return JOINER.join(map);
}
private static Parameter parse(String value) {
Map<String, String> m = SPLITTER.split(value);
return new Parameter(m);
}
}
}