docs: def bisect(a, b, fn, epsilon, target):
      """
      The simplest root-finding algorithm.
  
      It is extremely reliable. However, it converges slowly for this reason,
      it is recommended that this only be used after the secant() method has
      returned None.
  
      Args:
          a: A lower guess of the value you are tying to find.
          b: A higher guess of the value you are tying to find.
          fn: A function representing the relationship between the value you are
              trying to find and the target condition you are trying to satisfy.
              It should typically be structured in the following way:
              `def fn(value_trying_to_find):
                  funct(value_trying_to_find) - target_desired_from_funct`
              ...but the subtraction should be swtiched if value_trying_to_find
              has a negative relationship with the funct.
          epsilon: The acceptable error in the target_desired_from_funct.
          target: The target slope (typically 0 for a local minima or maxima).
  
      Returns:
          root: The value that gives the target_desired_from_funct.
  
      References
      ----------
      [1] Wikipedia contributors. (2018, December 29). Root-finding algorithm.
      In Wikipedia, The Free Encyclopedia. Retrieved 18:16, December 30, 2018,
      from https://en.wikipedia.org/wiki/Root-finding_algorithm#Bisection_method
      """
      while (abs(b - a) > 2 * epsilon):
          midpoint = (b + a) / 2
          a_t = fn(a)
          b_t = fn(b)
          midpoint_t = fn(midpoint)
          if (a_t - target) * (midpoint_t - target) < 0:
              b = midpoint
          elif (b_t - target) * (midpoint_t - target) < 0:
              a = midpoint
          else:
              return -999
  
      return midpoint