/* (non-Javadoc)
   * @see net.finmath.stochastic.RandomVariableInterface#addProduct(net.finmath.stochastic.RandomVariableInterface, double)
   */
  public RandomVariableInterface addProduct(RandomVariableInterface factor1, double factor2) {
    // Set time of this random variable to maximum of time with respect to which measurability is
    // known.
    double newTime = Math.max(time, factor1.getFiltrationTime());

    if (isDeterministic() && factor1.isDeterministic()) {
      double newValueIfNonStochastic = valueIfNonStochastic + (factor1.get(0) * factor2);
      return new RandomVariable(newTime, newValueIfNonStochastic);
    } else if (isDeterministic() && !factor1.isDeterministic()) {
      double[] factor1Realizations = factor1.getRealizations();
      double[] newRealizations = new double[Math.max(size(), factor1.size())];
      for (int i = 0; i < newRealizations.length; i++)
        newRealizations[i] = valueIfNonStochastic + factor1Realizations[i] * factor2;
      return new RandomVariable(newTime, newRealizations);
    } else if (!isDeterministic() && factor1.isDeterministic()) {
      double factor1Value = factor1.get(0);
      double[] newRealizations = new double[Math.max(size(), factor1.size())];
      for (int i = 0; i < newRealizations.length; i++)
        newRealizations[i] = realizations[i] + factor1Value * factor2;
      return new RandomVariable(newTime, newRealizations);
    } else {
      double[] factor1Realizations = factor1.getRealizations();
      double[] newRealizations = new double[Math.max(size(), factor1.size())];
      for (int i = 0; i < newRealizations.length; i++)
        newRealizations[i] = realizations[i] + factor1Realizations[i] * factor2;
      return new RandomVariable(newTime, newRealizations);
    }
  }
  /* (non-Javadoc)
   * @see net.finmath.stochastic.RandomVariableInterface#barrier(net.finmath.stochastic.RandomVariableInterface, net.finmath.stochastic.RandomVariableInterface, net.finmath.stochastic.RandomVariableInterface)
   */
  public RandomVariableInterface barrier(
      RandomVariableInterface trigger,
      RandomVariableInterface valueIfTriggerNonNegative,
      RandomVariableInterface valueIfTriggerNegative) {
    // Set time of this random variable to maximum of time with respect to which measurability is
    // known.
    double newTime = Math.max(time, trigger.getFiltrationTime());
    newTime = Math.max(newTime, valueIfTriggerNonNegative.getFiltrationTime());
    newTime = Math.max(newTime, valueIfTriggerNegative.getFiltrationTime());

    if (isDeterministic()
        && trigger.isDeterministic()
        && valueIfTriggerNonNegative.isDeterministic()
        && valueIfTriggerNegative.isDeterministic()) {
      double newValueIfNonStochastic =
          trigger.get(0) >= 0 ? valueIfTriggerNonNegative.get(0) : valueIfTriggerNegative.get(0);
      return new RandomVariable(newTime, newValueIfNonStochastic);
    } else {
      int numberOfPaths =
          Math.max(
              Math.max(trigger.size(), valueIfTriggerNonNegative.size()),
              valueIfTriggerNegative.size());
      double[] newRealizations = new double[numberOfPaths];
      for (int i = 0; i < newRealizations.length; i++) {
        newRealizations[i] =
            trigger.get(i) >= 0.0
                ? valueIfTriggerNonNegative.get(i)
                : valueIfTriggerNegative.get(i);
      }
      return new RandomVariable(newTime, newRealizations);
    }
  }
  /* (non-Javadoc)
   * @see net.finmath.stochastic.RandomVariableInterface#discount(net.finmath.stochastic.RandomVariableInterface, double)
   */
  public RandomVariableInterface discount(RandomVariableInterface rate, double periodLength) {
    // Set time of this random variable to maximum of time with respect to which measurability is
    // known.
    double newTime = Math.max(time, rate.getFiltrationTime());

    if (isDeterministic() && rate.isDeterministic()) {
      double newValueIfNonStochastic = valueIfNonStochastic / (1 + rate.get(0) * periodLength);
      return new RandomVariable(newTime, newValueIfNonStochastic);
    } else if (isDeterministic() && !rate.isDeterministic()) {
      double[] rateRealizations = rate.getRealizations();
      double[] newRealizations = new double[Math.max(size(), rate.size())];
      for (int i = 0; i < newRealizations.length; i++)
        newRealizations[i] = valueIfNonStochastic / (1.0 + rateRealizations[i] * periodLength);
      return new RandomVariable(newTime, newRealizations);
    } else if (!isDeterministic() && rate.isDeterministic()) {
      double rateValue = rate.get(0);
      double[] newRealizations = new double[Math.max(size(), rate.size())];
      for (int i = 0; i < newRealizations.length; i++)
        newRealizations[i] = realizations[i] / (1.0 + rateValue * periodLength);
      return new RandomVariable(newTime, newRealizations);
    } else {
      double[] rateRealizations = rate.getRealizations();
      double[] newRealizations = new double[Math.max(size(), rate.size())];
      for (int i = 0; i < newRealizations.length; i++)
        newRealizations[i] = realizations[i] / (1.0 + rateRealizations[i] * periodLength);
      return new RandomVariable(newTime, newRealizations);
    }
  }
  /* (non-Javadoc)
   * @see net.finmath.stochastic.RandomVariableInterface#subRatio(net.finmath.stochastic.RandomVariableInterface, net.finmath.stochastic.RandomVariableInterface)
   */
  public RandomVariableInterface subRatio(
      RandomVariableInterface numerator, RandomVariableInterface denominator) {
    // Set time of this random variable to maximum of time with respect to which measurability is
    // known.
    double newTime =
        Math.max(Math.max(time, numerator.getFiltrationTime()), denominator.getFiltrationTime());

    if (isDeterministic() && numerator.isDeterministic() && denominator.isDeterministic()) {
      double newValueIfNonStochastic =
          valueIfNonStochastic - (numerator.get(0) / denominator.get(0));
      return new RandomVariable(newTime, newValueIfNonStochastic);
    } else {
      double[] newRealizations =
          new double[Math.max(Math.max(size(), numerator.size()), denominator.size())];
      for (int i = 0; i < newRealizations.length; i++)
        newRealizations[i] = get(i) - numerator.get(i) / denominator.get(i);
      return new RandomVariable(newTime, newRealizations);
    }
  }
  /* (non-Javadoc)
   * @see net.finmath.stochastic.RandomVariableInterface#div(net.finmath.stochastic.RandomVariableInterface)
   */
  public RandomVariableInterface div(RandomVariableInterface randomVariable) {
    // Set time of this random variable to maximum of time with respect to which measurability is
    // known.
    double newTime = Math.max(time, randomVariable.getFiltrationTime());

    if (isDeterministic() && randomVariable.isDeterministic()) {
      double newValueIfNonStochastic = valueIfNonStochastic / randomVariable.get(0);
      return new RandomVariable(newTime, newValueIfNonStochastic);
    } else if (isDeterministic()) {
      double[] newRealizations = new double[Math.max(size(), randomVariable.size())];
      for (int i = 0; i < newRealizations.length; i++)
        newRealizations[i] = valueIfNonStochastic / randomVariable.get(i);
      return new RandomVariable(newTime, newRealizations);
    } else {
      double[] newRealizations = new double[Math.max(size(), randomVariable.size())];
      for (int i = 0; i < newRealizations.length; i++)
        newRealizations[i] = realizations[i] / randomVariable.get(i);
      return new RandomVariable(newTime, newRealizations);
    }
  }