docs: private void doPrecalculateProcess()
  	{
  		if(discreteProcess != null && discreteProcess.length != 0) {
  			return;
  		}
  
  		// Allocate Memory
  		discreteProcess			= new RandomVariableInterface[timeDiscretization.getNumberOfTimeSteps()+1][numberOfComponents];
  		discreteProcessWeights	= new RandomVariableInterface[getTimeDiscretization().getNumberOfTimeSteps()+1];
  
  		// Set initial Monte-Carlo weights
  		discreteProcessWeights[0] = new RandomVariable(0.0, 1.0/numberOfPaths);
  
  		// Store components
  		discreteProcess[0] = getInitialValue();
  
  		// Evolve process
  		for(int timeIndex = 1; timeIndex < timeDiscretization.getNumberOfTimeSteps()+1; timeIndex++)
  		{
  			// Generate process at timeIndex
  			double deltaT = timeDiscretization.getTime(timeIndex) - timeDiscretization.getTime(timeIndex-1);
  
  			// Temp storage for variance and diffusion
  			RandomVariableInterface[] variance   = new RandomVariableInterface[numberOfComponents];
  			RandomVariableInterface[] diffusion  = new RandomVariableInterface[numberOfComponents];
  
  			// Calculate new realization
  			for(int componentIndex = numberOfComponents-1; componentIndex >= 0; componentIndex--)
  			{
  				// Calculate diffusion
  
  				// Temp storage for variance and diffusion
  				RandomVariableInterface varianceOfComponent  = new RandomVariable(getTime(timeIndex-1),0.0);
  				RandomVariableInterface diffusionOfComponent = new RandomVariable(getTime(timeIndex-1),0.0);
  
  				// Generate values for diffusionOfComponent and varianceOfComponent
  				for(int factor=0; factor<numberOfFactors; factor++) {
  					RandomVariableInterface factorLoading       = getFactorLoading(timeIndex-1, factor, componentIndex, null);
  					RandomVariableInterface brownianIncrement   = brownianMotion.getBrownianIncrement(timeIndex-1,factor);
  
  					varianceOfComponent = varianceOfComponent.addProduct(factorLoading, factorLoading);
  					diffusionOfComponent = diffusionOfComponent.addProduct(factorLoading, brownianIncrement);
  				}
  
  				variance[componentIndex] = varianceOfComponent;
  				diffusion[componentIndex] = diffusionOfComponent;
  			}
  
  			RandomVariableInterface[] drift;
  			if(scheme == Scheme.PREDICTOR_USING_LASTREALIZATION) {
  				drift = getDrift(timeIndex-1, discreteProcess[timeIndex-1], discreteProcess[timeIndex]);
  			} else {
  				drift = getDrift(timeIndex-1, discreteProcess[timeIndex-1], null                      );
  			}
  
  			// Calculate drift
  			for(int componentIndex = numberOfComponents-1; componentIndex >= 0; componentIndex--)
  			{
  				RandomVariableInterface driftOfComponent  	= drift[componentIndex];
  				RandomVariableInterface varianceOfComponent			= variance[componentIndex];
  				RandomVariableInterface diffusionOfComponent		= diffusion[componentIndex];
  
  				if(driftOfComponent == null) {
  					discreteProcess[timeIndex][componentIndex] = discreteProcess[timeIndex-1][componentIndex];
  					continue;
  				}
  
  				// Allocate memory for on path-realizations
  				double[] newRealization			= new double[numberOfPaths];
  
  				// Euler Scheme
  				RandomVariableInterface previouseRealization		= discreteProcess[timeIndex-1][componentIndex];
  
  				// Generate values
  				for(int pathIndex = 0; pathIndex < numberOfPaths; pathIndex++)
  				{
  					double previousValue	= previouseRealization.get(pathIndex);
  					double driftOnPath		= driftOfComponent.get(pathIndex);
  					double varianceOnPath	= varianceOfComponent.get(pathIndex);
  					double diffusionOnPath	= diffusionOfComponent.get(pathIndex);
  
  					// The scheme
  					newRealization[pathIndex]	 = previousValue * Math.exp(driftOnPath * deltaT - 0.5 * varianceOnPath * deltaT + diffusionOnPath);
  				}
  
  				// Store components
  				discreteProcess[timeIndex][componentIndex] = new RandomVariable(getTime(timeIndex),newRealization);
  			}
  
  			if(scheme == Scheme.PREDICTOR_USING_EULERSTEP) {
  				RandomVariableInterface[] newRealization = new RandomVariable[numberOfComponents];
  
  				// Note: This is actually more than a predictor corrector: The drift of componentIndex already uses the corrected predictor from the previous components
  				drift = getDrift(timeIndex-1, discreteProcess[timeIndex-1], discreteProcess[timeIndex]);
  
  				// Apply corrector step to realizations at next time step
  				for(int componentIndex = 0; componentIndex < numberOfComponents; componentIndex++)
  				{
  					RandomVariableInterface driftOfComponent	= drift[componentIndex];
  					RandomVariableInterface varianceOfComponent			= variance[componentIndex];
  					RandomVariableInterface diffusionOfComponent		= diffusion[componentIndex];
  
  					// Euler Scheme with corrected drift
  					RandomVariableInterface previouseRealization 	= discreteProcess[timeIndex-1][componentIndex];
  
  					// The scheme
  					// newValue = previousValue * Math.exp(driftValue * deltaT - 0.5 * varianceOnPath * deltaT + diffusionOnPath);
  					newRealization[componentIndex] = previouseRealization.mult((driftOfComponent.mult(deltaT).sub(varianceOfComponent.mult(0.5 * deltaT)).add(diffusionOfComponent)).exp());
  				} // End for(componentIndex)
  
  				// Store predictor-corrector corrected process.
  				discreteProcess[timeIndex] = newRealization;
  			} // End if(scheme == LogNormalProcess.SCHEME_PREDICTOR_USES_EULER)
  
  			// Set Monte-Carlo weights (since there is no Monte-Carlo weighting, the weights remain the same (namely 1.0/numberOfPaths).
  			discreteProcessWeights[timeIndex] = discreteProcessWeights[timeIndex-1];
  		} // End for(timeIndex)
  	}