/**
 * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
 * 
 * Please see distribution for license.
 */
package com.opengamma.analytics.math.minimization;

import org.apache.commons.lang.Validate;

import com.opengamma.analytics.math.FunctionUtils;
import com.opengamma.analytics.math.TrigonometricFunctionUtils;

/**
 * If a model parameter $x$ is constrained to be between two values $a \geq x
 * \geq b$, the function to transform it to an unconstrained variable is $y$ is
 * given by
 * $$
 * \begin{align*}
 * y &= \tanh^{-1}\left(\frac{x - m}{s}\right)\\
 * m &= \frac{a + b}{2}\\
 * s &= \frac{b - a}{2}
 * \end{align*}
 * $$
 * with the inverse transform
 * $$
 * \begin{align*}
 * x &= s\tanh(y) + m\\
 * \end{align*}
 * $$
 */
public class DoubleRangeLimitTransform implements ParameterLimitsTransform {
  private static final double TANH_MAX = 25.0;
  private final double _lower;
  private final double _upper;
  private final double _scale;
  private final double _mid;

  /**
   * @param lower Lower limit
   * @param upper Upper limit
   * @throws IllegalArgumentException If the upper limit is not greater than the lower limit
   */
  public DoubleRangeLimitTransform(final double lower, final double upper) {
    Validate.isTrue(upper > lower, "upper limit must be greater than lower");
    _lower = lower;
    _upper = upper;
    _mid = (lower + upper) / 2;
    _scale = (upper - lower) / 2;
  }

  /**
   * If $y > 25$, this returns $b$. If $y < -25$ returns $a$.
   * {@inheritDoc}
   */
  @Override
  public double inverseTransform(final double y) {
    if (y > TANH_MAX) {
      return _upper;
    } else if (y < -TANH_MAX) {
      return _lower;
    }
    return _mid + _scale * TrigonometricFunctionUtils.tanh(y);
  }

  /**
   * {@inheritDoc}
   * @throws IllegalArgumentException If $x > b$ or $x < a$
   */
  @Override
  public double transform(final double x) {
    Validate.isTrue(x <= _upper && x >= _lower, "parameter out of range");
    if (x == _upper) {
      return TANH_MAX;
    } else if (x == _lower) {
      return -TANH_MAX;
    }
    return TrigonometricFunctionUtils.atanh((x - _mid) / _scale);
  }

  /**
   * If $|y| > 25$, this returns 0.
   * {@inheritDoc}
   */
  @Override
  public double inverseTransformGradient(final double y) {
    if (y > TANH_MAX || y < -TANH_MAX) {
      return 0.0;
    }
    double p = 2 * y;
    double ep = Math.exp(p);
    double epp1 = ep + 1;
    return _scale * 4 * ep / (epp1 * epp1);
  }

  /**
   * {@inheritDoc}
   * @throws IllegalArgumentException If $x > b$ or $x < a$
   */
  @Override
  public double transformGradient(final double x) {
    Validate.isTrue(x <= _upper && x >= _lower, "parameter out of range");
    final double t = (x - _mid) / _scale;
    return 1 / (_scale * (1 - t * t));
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    long temp;
    temp = Double.doubleToLongBits(_lower);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    temp = Double.doubleToLongBits(_upper);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    return result;
  }

  @Override
  public boolean equals(final Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    final DoubleRangeLimitTransform other = (DoubleRangeLimitTransform) obj;
    if (Double.doubleToLongBits(_lower) != Double.doubleToLongBits(other._lower)) {
      return false;
    }
    return Double.doubleToLongBits(_upper) == Double.doubleToLongBits(other._upper);
  }

}

@@ -230,59 +230,8 @@
 e;%0A%0A
-import com.opengamma.analytics.math.FunctionUtils;%0A
 impo
@@ -2322,24 +2322,30 @@
 ;%0A    %7D%0A    
+final 
 double p = 2
@@ -2350,24 +2350,30 @@
  2 * y;%0A    
+final 
 double ep = 
@@ -2389,16 +2389,22 @@
 p);%0A    
+final 
 double e

Scale factor was removed accidentally