docs: @Override
  	public RandomVariableInterface getNumeraire(double time) throws CalculationException {
  		int timeIndex = getLiborPeriodIndex(time);
  
  		if(timeIndex < 0) {
  			// Interpolation of Numeraire: log linear interpolation.
  			int upperIndex = -timeIndex-1;
  			int lowerIndex = upperIndex-1;
  			if(lowerIndex < 0) {
  				throw new IllegalArgumentException("Numeraire requested for time " + time + ". Unsupported");
  			}
  
  			double alpha = (time-getLiborPeriod(lowerIndex)) / (getLiborPeriod(upperIndex) - getLiborPeriod(lowerIndex));
  			RandomVariableInterface numeraire = getNumeraire(getLiborPeriod(upperIndex)).log().mult(alpha).add(getNumeraire(getLiborPeriod(lowerIndex)).log().mult(1.0-alpha)).exp();
  
  			/*
  			 * Adjust for discounting, i.e. funding or collateralization
  			 */
  			if(discountCurve != null) {
  				// This includes a control for zero bonds
  				double deterministicNumeraireAdjustment = numeraire.invert().getAverage() / discountCurve.getDiscountFactor(curveModel, time);
  				numeraire = numeraire.mult(deterministicNumeraireAdjustment);
  			}
  
  			return numeraire;
  		}
  
  		/*
  		 * Calculate the numeraire, when time is part of liborPeriodDiscretization
  		 */
  
  		/*
  		 * Check if numeraire cache is valid (i.e. process did not change)
  		 */
  		if(getProcess() != numerairesProcess) {
  			numeraires.clear();
  			numerairesProcess = getProcess();
  		}
  
  		/*
  		 * Check if numeraire is part of the cache
  		 */
  		RandomVariableInterface numeraire = numeraires.get(timeIndex);
  		if(numeraire == null) {
  			/*
  			 * Calculate the numeraire for timeIndex
  			 */
  
  			// Initialize to 1.0
  			numeraire = getRandomVariableForConstant(1.0);
  
  
  			// Get the start and end of the product
  			int firstLiborIndex, lastLiborIndex;
  
  			if(measure == Measure.TERMINAL) {
  				firstLiborIndex	= getLiborPeriodIndex(time);
  				if(firstLiborIndex < 0) {
  					throw new CalculationException("Simulation time discretization not part of forward rate tenor discretization.");
  				}
  
  				lastLiborIndex 	= liborPeriodDiscretization.getNumberOfTimeSteps()-1;
  			}
  			else if(measure == Measure.SPOT) {
  				// Spot measure
  				firstLiborIndex	= 0;
  				lastLiborIndex	= getLiborPeriodIndex(time)-1;
  			}
  			else {
  				throw new CalculationException("Numeraire not implemented for specified measure.");
  			}
  
  			// The product
  			for(int liborIndex = firstLiborIndex; liborIndex<=lastLiborIndex; liborIndex++) {
  				RandomVariableInterface libor = getLIBOR(getTimeIndex(Math.min(time,liborPeriodDiscretization.getTime(liborIndex))), liborIndex);
  
  				double periodLength = liborPeriodDiscretization.getTimeStep(liborIndex);
  
  				if(measure == Measure.SPOT) {
  					numeraire = numeraire.accrue(libor, periodLength);
  				}
  				else {
  					numeraire = numeraire.discount(libor, periodLength);
  				}
  			}
  			numeraires.put(timeIndex, numeraire);
  		}
  
  		/*
  		 * Adjust for discounting, i.e. funding or collateralization
  		 */
  		if(discountCurve != null) {
  			// This includes a control for zero bonds
  			double deterministicNumeraireAdjustment = numeraire.invert().getAverage() / discountCurve.getDiscountFactor(curveModel, time);
  			numeraire = numeraire.mult(deterministicNumeraireAdjustment);
  		}
  		return numeraire;
  	}