/**
  * Return the complement of the interior of the interval. An interval and its
  * complement have the same boundary but do not share any interior values. The
  * complement operator is not a bijection, since the complement of a singleton
  * interval (containing a single value) is the same as the complement of an
  * empty interval.
  *#/
  * public S1Interval complement() {
  * if (lo() == hi()) {
  * return full(); // Singleton.
  * }
  * return new S1Interval(hi(), lo(), true); // Handles
  * // empty and
  * // full.
  * }
  *
  * /** Return true if the interval (which is closed) contains the point 'p'. *#/
  * public boolean contains(double p) {
  * // Works for empty, full, and singleton intervals.
  * // assert (Math.abs(p) <= S2.M_PI);
  * if (p == -S2.M_PI) {
  * p = S2.M_PI;
  * }
  * return fastContains(p);
  * }
  *
  * /**
  * Return true if the interval (which is closed) contains the point 'p'. Skips
  * the normalization of 'p' from -Pi to Pi.
  *
  *#/
  * public boolean fastContains(double p) {
  * if (isInverted()) {
  * return (p >= lo() || p <= hi()) && !isEmpty();
  * } else {
  * return p >= lo() && p <= hi();
  * }
  * }
  *
  * /** Return true if the interior of the interval contains the point 'p'. *#/
  * public boolean interiorContains(double p) {
  * // Works for empty, full, and singleton intervals.
  * // assert (Math.abs(p) <= S2.M_PI);
  * if (p == -S2.M_PI) {
  * p = S2.M_PI;
  * }
  *
  * if (isInverted()) {
  * return p > lo() || p < hi();
  * } else {
  * return (p > lo() && p < hi()) || isFull();
  * }
  * }
  *
  * /**
  * Return true if the interval contains the given interval 'y'. Works for
  * empty, full, and singleton intervals.
  *#/
  * public boolean contains(final S1Interval y) {
  * // It might be helpful to compare the structure of these tests to
  * // the simpler Contains(double) method above.
  *
  * if (isInverted()) {
  * if (y.isInverted()) {
  * return y.lo() >= lo() && y.hi() <= hi();
  * }
  * return (y.lo() >= lo() || y.hi() <= hi()) && !isEmpty();
  * } else {
  * if (y.isInverted()) {
  * return isFull() || y.isEmpty();
  * }
  * return y.lo() >= lo() && y.hi() <= hi();
  * }
  * }
  *
  * /**
  * Returns true if the interior of this interval contains the entire interval
  * 'y'. Note that x.InteriorContains(x) is true only when x is the empty or
  * full interval, and x.InteriorContains(S1Interval(p,p)) is equivalent to
  * x.InteriorContains(p).
  *#/
  * public boolean interiorContains(final S1Interval y) {
  * if (isInverted()) {
  * if (!y.isInverted()) {
  * return y.lo() > lo() || y.hi() < hi();
  * }
  * return (y.lo() > lo() && y.hi() < hi()) || y.isEmpty();
  * } else {
  * if (y.isInverted()) {
  * return isFull() || y.isEmpty();
  * }
  * return (y.lo() > lo() && y.hi() < hi()) || isFull();
  * }
  * }
  *
  * /**
  * Return true if the two intervals contain any points in common. Note that
  * the point +/-Pi has two representations, so the intervals [-Pi,-3] and
  * [2,Pi] intersect, for example.
  */
 public function intersects(S1Interval $y)
 {
     if ($this->isEmpty() || $y->isEmpty()) {
         return false;
     }
     if ($this->isInverted()) {
         // Every non-empty inverted interval contains Pi.
         return $y->isInverted() || $y->lo() <= $this->hi() || $y->hi() >= $this->lo();
     } else {
         if ($y->isInverted()) {
             return $y->lo() <= $this->hi() || $y->hi() >= $this->lo();
         }
         return $y->lo() <= $this->hi() && $y->hi() >= $this->lo();
     }
 }