docs: public static Map<String, double[]> getLogSwaprateDerivative(TimeDiscretization liborPeriodDiscretization, DiscountCurve discountCurve, ForwardCurve forwardCurve, double[] swapTenor) {
  		/*
  		 * Small workaround for the case that the discount curve is not set. This part will be removed later.
  		 */
  		AnalyticModelFromCurvesAndVols model = null;
  		if(discountCurve == null) {
  			discountCurve	= new DiscountCurveFromForwardCurve(forwardCurve.getName());
  			model					= new AnalyticModelFromCurvesAndVols(new Curve[] { forwardCurve, discountCurve });
  		}
  
  		double swapStart    = swapTenor[0];
  		double swapEnd      = swapTenor[swapTenor.length-1];
  
  		// Get the indices of the swap start and end on the forward rate tenor
  		int swapStartIndex  = liborPeriodDiscretization.getTimeIndex(swapStart);
  		int swapEndIndex    = liborPeriodDiscretization.getTimeIndex(swapEnd);
  
  		// Precalculate forward rates and discount factors. Note: the swap contains swapEndIndex-swapStartIndex forward rates
  		double[] forwardRates       = new double[swapEndIndex-swapStartIndex+1];
  		double[] discountFactors    = new double[swapEndIndex-swapStartIndex+1];
  
  		// Calculate discount factor at swap start
  		discountFactors[0] = discountCurve.getDiscountFactor(model, swapStart);
  
  		// Calculate discount factors for swap period ends (used for swap annuity)
  		for(int liborPeriodIndex = swapStartIndex; liborPeriodIndex < swapEndIndex; liborPeriodIndex++) {
  			double libor = forwardCurve.getForward(null, liborPeriodDiscretization.getTime(liborPeriodIndex));
  
  			forwardRates[liborPeriodIndex-swapStartIndex]       = libor;
  			discountFactors[liborPeriodIndex-swapStartIndex+1]  = discountCurve.getDiscountFactor(model, liborPeriodDiscretization.getTime(liborPeriodIndex+1));
  		}
  
  		// Precalculate swap annuities
  		double[]    swapAnnuities   = new double[swapTenor.length-1];
  		double      swapAnnuity     = 0.0;
  		for(int swapPeriodIndex = swapTenor.length-2; swapPeriodIndex >= 0; swapPeriodIndex--) {
  			int periodEndIndex = liborPeriodDiscretization.getTimeIndex(swapTenor[swapPeriodIndex+1]);
  			swapAnnuity += discountFactors[periodEndIndex-swapStartIndex] * (swapTenor[swapPeriodIndex+1]-swapTenor[swapPeriodIndex]);
  			swapAnnuities[swapPeriodIndex] = swapAnnuity;
  		}
  
  		int[] liborPeriodsInSwapPeriods = new int[swapTenor.length-1];
  		Arrays.fill(liborPeriodsInSwapPeriods, 0);
  		for(int liborPeriodIndex = swapStartIndex, swapPeriodIndex = 0; liborPeriodIndex < swapEndIndex; liborPeriodIndex++) {
  			if(liborPeriodDiscretization.getTime(liborPeriodIndex) >= swapTenor[swapPeriodIndex+1]) {
  				swapPeriodIndex++;
  			}
  			liborPeriodsInSwapPeriods[swapPeriodIndex]++;
  		}
  
  		// Precalculate weights: The formula is the one by Rebonato (note: this formula can be improved easily, we provide it rather for testing/illustration.
  		double[] swapCovarianceWeights = new double[swapEndIndex-swapStartIndex];
  
  		for(int liborPeriodIndex = swapStartIndex, swapPeriodIndex = 0; liborPeriodIndex < swapEndIndex; liborPeriodIndex++) {
  			if(liborPeriodDiscretization.getTime(liborPeriodIndex) >= swapTenor[swapPeriodIndex+1]) {
  				swapPeriodIndex++;
  			}
  
  			double swapAnnuityCurrent	= swapAnnuities[swapPeriodIndex];
  			double swapAnnuityNext		= swapPeriodIndex < swapAnnuities.length-1 ? swapAnnuities[swapPeriodIndex+1] : 0;
  
  			swapCovarianceWeights[liborPeriodIndex-swapStartIndex] = (swapAnnuityCurrent-swapAnnuityNext)/swapAnnuity/ liborPeriodsInSwapPeriods[swapPeriodIndex];
  		}
  
  		// Return results
  		Map<String, double[]> results = new HashMap<>();
  		results.put("values",			swapCovarianceWeights);
  		results.put("discountFactors",	discountFactors);
  		results.put("swapAnnuities",	swapAnnuities);
  
  		return results;
  	}