class Histogram {

    protected static <T> List<T> getBucketBoundaries( MathOperations<T> math,
                                                      T lowerBound,
                                                      T upperBound,
                                                      T actualMinimum,
                                                      T actualMaximum,
                                                      int bucketCount,
                                                      int bucketWidthSigFigs ) {
        lowerBound = math.compare(lowerBound, actualMinimum) < 0 ? actualMinimum : lowerBound;
        upperBound = math.compare(actualMaximum, upperBound) < 0 ? actualMaximum : upperBound;
        if (math.compare(lowerBound, upperBound) == 0) {
            List<T> boundaries = new ArrayList<T>();
            boundaries.add(lowerBound);
            boundaries.add(upperBound);
            return boundaries;
        }
        final boolean extraLowerBucketNeeded = math.compare(lowerBound, actualMinimum) > 0;
        final boolean extraUpperBucketNeeded = math.compare(actualMaximum, upperBound) > 0;
        if (extraLowerBucketNeeded) --bucketCount;
        if (extraUpperBucketNeeded) --bucketCount;

        // Compute the delta between the lower and upper bound ...
        T totalWidth = math.subtract(upperBound, lowerBound);
        int totalWidthScale = math.getExponentInScientificNotation(totalWidth);

        // Modify the lower bound by rounding down to the next lower meaningful value,
        // using the scale of the totalWidth to determine how to round down.
        T roundedLowerBound = math.roundDown(lowerBound, -totalWidthScale);
        T roundedUpperBound = math.roundUp(upperBound, -totalWidthScale);

        // Create the ranges ...
        double finalLowerBound = math.doubleValue(roundedLowerBound);
        double finalUpperBound = math.doubleValue(roundedUpperBound);
        double finalBucketCount = bucketCount;
        double bucketWidth = (finalUpperBound - finalLowerBound) / finalBucketCount;

        // DoubleOperations doubleOps = new DoubleOperations();
        // bucketWidth = doubleOps.keepSignificantFigures(bucketWidth,bucketWidthSigFigs);

        List<T> boundaries = new ArrayList<T>();
        if (bucketWidth > 0.0d) {
            if (extraLowerBucketNeeded) boundaries.add(actualMinimum);
            double nextBoundary = finalLowerBound;
            for (int i = 0; i != bucketCount; ++i) {
                boundaries.add(math.create(nextBoundary));
                nextBoundary = nextBoundary + bucketWidth;
                // nextBoundary = doubleOps.roundUp(nextBoundary + bucketWidth, bucketWidthSigFigs );
            }
            boundaries.add(roundedUpperBound);
            if (extraUpperBucketNeeded) boundaries.add(actualMaximum);
        }
        return boundaries;
    }

    public  Histogram( MathOperations<T> operations,
                      List<T> values );
    @SafeVarargs public  Histogram( MathOperations<T> operations, T... values );

}