SegmentGraphUtilities { public static <T> double accumulateValueTo(List<T> segments, ToDoubleFunction<T> segmentToXExtent, ToDoubleFunction<T> segmentToYExtent, BiFunction<T, Double, Double> yValueInterpolator, double xValue) { double xValueAtBoundary = 0; double yValueAtBoundary = 0; for (T segment : segments) { double xExtent = segmentToXExtent.applyAsDouble(segment); double xDistanceIntoSegment = Math.max(xValue - xValueAtBoundary, 0); if (xExtent >= xDistanceIntoSegment) { return yValueAtBoundary + yValueInterpolator.apply(segment, xDistanceIntoSegment); } xValueAtBoundary += xExtent; yValueAtBoundary += segmentToYExtent.applyAsDouble(segment); } if (xValue - xValueAtBoundary < xValue * ERROR_TOLERANCE) { return yValueAtBoundary; } throw Failer.fail("Should have found an answer in loop"); } private  SegmentGraphUtilities(); static double getValueAt(List<T> segments, ToDoubleFunction<T> segmentToXExtent, BiFunction<T, Double, Double> yValueInterpolator, double xValue); static double accumulateValueTo(List<T> segments, ToDoubleFunction<T> segmentToXExtent, ToDoubleFunction<T> segmentToYExtent, BiFunction<T, Double, Double> yValueInterpolator, double xValue);  }
@Test void accumulateValueTo_whenXWithinRange_thenReturnsExpectedValue() { double result = SegmentGraphUtilities.accumulateValueTo(sections, Section::xExtent, s -> s.y, Section::extrapolateValue, 7.7); assertThat(result).isCloseTo(9.4, within(1e-6)); }
@Test void accumulateValueTo_whenXBeyondRange_thenThrowsException() { assertThatThrownBy(() -> SegmentGraphUtilities.accumulateValueTo(sections, Section::xExtent, s -> s.y, Section::extrapolateValue, 100000)) .isInstanceOf(IllegalStateException.class); }