-------------TO FIND THE INTERPOLATION INDEX----------------
SUBROUTINE binary_search(head, tail, needle, haystack, size, decreasing)Find the insert position for the needle that preserves the ordering of the
haystack. Comparing to the regular binary search, this subroutine returns
a pair of indices indicating the insertion interval. If the needle is
found in the haystack, those indices coincide.
  IMPLICIT NONE
  INTEGER, INTENT(out) :: head
  INTEGER, INTENT(out) :: tail
  DOUBLE PRECISION, INTENT(in) :: needle
  INTEGER :: sizef2py INTEGER, INTENT(hide), DEPEND(haystack) :: size = len(haystack)
  DOUBLE PRECISION, INTENT(in), DIMENSION(size) :: haystack
  LOGICAL, INTENT(in) :: decreasingf2py LOGICAL, INTENT(in), OPTIONAL :: decreasing = 0
  INTEGER range
  INTEGER middle
  DOUBLE PRECISION comparison
  IF (decreasing) THEN
    comparison = -1.
  ELSE
    comparison = 1.
  END IF
  head = 1
  tail = sizeCheck if the needle is outside the haystack range
  IF (SIGN(1., needle - haystack(head)) == -comparison) THEN
    tail = head
    RETURN
  ELSE IF (SIGN(1., needle - haystack(tail)) == comparison) THEN
    head = tail
    RETURN
  END IF
  range = tail - head
  middle = (tail + head) / 2
  DO WHILE ( haystack(middle) /= needle .AND. range > 1)
    IF (SIGN(1., needle - haystack(middle)) == comparison) THEN
      head = middle
    ELSE
      tail = middle
    END IF
    range = tail - head
    middle = (tail + head) / 2
  END DOIdentify if the needle is positioned exactly at middle/head/tail
  IF (haystack(middle) == needle) THEN
    head = middle
    tail = middle
  ELSE IF (haystack(head) == needle) THEN
    tail = head
  ELSE IF (haystack(tail) == needle) THEN
    head = tail
  END IF-------THE FUNCTIONS DOING AN INTERPOLATION---------------
SUBROUTINE interp_values(interp_val, x_interp, x_values, y_values, decreasing, size)
  IMPLICIT NONE
  DOUBLE PRECISION, INTENT(out) :: interp_valThe integer giving an x-value just below the one to be interpolated
  DOUBLE PRECISION :: x_lo, x_up
  DOUBLE PRECISION :: y0, y1, y2, y3
  DOUBLE PRECISION :: a0, a1, a2, a3, mu
  CALL binary_search(head, tail, x_interp, x_values, size, decreasing)
  IF (head == tail) THEN
    interp_val = y_values(head)
  ELSE
    IF (head == 1 .OR. tail == size) THENIf the value lies near the end of the interpolated region, use simple linear interpolation
      x_lo = x_values(head)
      x_up = x_values(tail)
      interp_val = ( (x_up-x_interp)*y_values(head) + (x_interp-x_lo)*y_values(tail) ) / (x_up - x_lo)Otherwise, use cubic interpolation
      y0 = y_values(head - 1)
      y1 = y_values(head)
      y2 = y_values(tail)
      y3 = y_values(tail + 1)
      a0 = -0.5*y0 + 1.5*y1 - 1.5*y2 + 0.5*y3;
      a1 = y0 - 2.5*y1 + 2*y2 - 0.5*y3;
      a2 = -0.5*y0 + 0.5*y2;
      a3 = y1;
      mu = (x_interp - x_values(head)) / (x_values(tail) - x_values(head))
      interp_val = a0*mu*mu*mu + a1*mu*mu + a2*mu + a3--------LOGARITHMIC INTERPOLATION--------------------- We will usually do an interpolation of the log of the values as these values are rapidly varying, instead of simply doing a linear interp.
SUBROUTINE log_interp_values(interp_val, x_interp, x_values, y_values, decreasing, size)
  IMPLICIT NONE
  DOUBLE PRECISION, INTENT(out) :: interp_valThe x-value at which interpolation happens
  DOUBLE PRECISION, INTENT(in), DIMENSION(size) :: x_valuesThis tells if the y_values are positive, needed when taking the log.
  DOUBLE PRECISION x_lo, x_up, y_lo, y_up
  CALL binary_search(head, tail, x_interp, x_values, size, decreasing)
  IF (head == tail) THEN
    interp_val = y_values(head)
  ELSE
    y_lo = y_values(head)
    y_up = y_values(tail)
    IF ( y_lo*y_up <= 0 ) THENIt is not possible to take the log if one of them is 0 or if they have different signs, so we do a linear interpolation in this case
       CALL interp_values(interp_val, x_interp, x_values, y_values, decreasing, size)    ELSE
       positive = .true.
       IF (y_lo < 0) THEN
          positive = .false.
          y_lo = -y_lo        !For negative numbers we take the log
          y_up = -y_up        ! of their opposite
       END IF
       y_lo = log(y_lo)
       y_up = log(y_up)
       x_lo = x_values(head)
       x_up = x_values(tail)
       interp_val = ( (x_up-x_interp)*y_lo + (x_interp-x_lo)*y_up ) / (x_up - x_lo)
       interp_val = exp(interp_val)
       IF (.not.positive) THEN
          interp_val = -interp_val
       END IF
    END IF--------DISTRIBUTION FUNCTION LOGARITHMIC INTERPOLATION--------------------- We will usually do an interpolation of the log of the values as these values are rapidly varying, instead of simply doing a linear interp.
SUBROUTINE dist_interp_values(interp_val, x_interp, x_values, y_values, decreasing, size)
  IMPLICIT NONE
  DOUBLE PRECISION, INTENT(out) :: interp_valThe x-value at which interpolation happens
  DOUBLE PRECISION, INTENT(in), DIMENSION(size) :: x_valuesThis tells if the y_values are positive, needed when taking the log.
  DOUBLE PRECISION x_lo, x_up, y_lo, y_up
  CALL binary_search(head, tail, x_interp, x_values, size, decreasing)
  IF (head == tail) THEN
    interp_val = y_values(head)
  ELSE
    y_lo = y_values(head)
    y_up = y_values(tail)
    IF ( y_lo*y_up <= 0 ) THENIt is not possible to take the log if one of them is 0 or if they have different signs, so we do a linear interpolation in this case
       CALL interp_values(interp_val, x_interp, x_values, y_values, decreasing, size)    ELSE
       positive = .true.
       IF (y_lo < 0) THEN
          positive = .false.
          y_lo = -y_lo        !For negative numbers we take the log
          y_up = -y_up        ! of their opposite
       END IF
       y_lo = log(1 / y_lo - 1)
       y_up = log(1 / y_up - 1)
       x_lo = x_values(head)
       x_up = x_values(tail)
       interp_val = ( (x_up-x_interp)*y_lo + (x_interp-x_lo)*y_up ) / (x_up - x_lo)
       interp_val = 1 / (exp(interp_val) + 1)
       IF (.not.positive) THEN
          interp_val = -interp_val
       END IF
    END IF