class Builder extends StatefulMetric.Builder<Summary.Builder, Summary> {

        /**
         * 5 minutes. See {@link #maxAgeSeconds(long)}.
         */
        public static final long DEFAULT_MAX_AGE_SECONDS = TimeUnit.MINUTES.toSeconds(5);

        /**
         * 5. See {@link #numberOfAgeBuckets(int)}
         */
        public static final int DEFAULT_NUMBER_OF_AGE_BUCKETS = 5;
        private final List<CKMSQuantiles.Quantile> quantiles = new ArrayList<>();
        private Long maxAgeSeconds;
        private Integer ageBuckets;

        private Builder(PrometheusProperties properties) {
            super(Collections.singletonList("quantile"), properties);
        }

        private static double defaultError(double quantile) {
            if (quantile <= 0.01 || quantile >= 0.99) {
                return 0.001;
            } else if (quantile <= 0.02 || quantile >= 0.98) {
                return 0.005;
            } else {
                return 0.01;
            }
        }

        /**
         * Add a quantile. See {@link #quantile(double, double)}.
         * <p>
         * Default errors are:
         * <ul>
         *     <li>error = 0.001 if quantile &lt;= 0.01 or quantile &gt;= 0.99</li>
         *     <li>error = 0.005 if quantile &lt;= 0.02 or quantile &gt;= 0.98</li>
         *     <li>error = 0.01 else.
         * </ul>
         */
        public Builder quantile(double quantile) {
            return quantile(quantile, defaultError(quantile));
        }

        /**
         * Add a quantile. Call multiple times to add multiple quantiles.
         * <p>
         * Example: The following will track the 0.95 quantile:
         * <pre>{@code
         * .quantile(0.95, 0.001)
         * }</pre>
         * The second argument is the acceptable error margin, i.e. with the code above the quantile
         * will not be exactly the 0.95 quantile but something between 0.949 and 0.951.
         * <p>
         * There are two special cases:
         * <ul>
         *     <li>{@code .quantile(0.0, 0.0)} gives you the minimum observed value</li>
         *     <li>{@code .quantile(1.0, 0.0)} gives you the maximum observed value</li>
         * </ul>
         */
        public Builder quantile(double quantile, double error) {
            if (quantile < 0.0 || quantile > 1.0) {
                throw new IllegalArgumentException("Quantile " + quantile + " invalid: Expected number between 0.0 and 1.0.");
            }
            if (error < 0.0 || error > 1.0) {
                throw new IllegalArgumentException("Error " + error + " invalid: Expected number between 0.0 and 1.0.");
            }
            quantiles.add(new CKMSQuantiles.Quantile(quantile, error));
            return this;
        }

        /**
         * The quantiles are relative to a moving time window.
         * {@code maxAgeSeconds} is the size of that time window.
         * Default is {@link #DEFAULT_MAX_AGE_SECONDS}.
         */
        public Builder maxAgeSeconds(long maxAgeSeconds) {<FILL_FUNCTION_BODY>}

        /**
         * The quantiles are relative to a moving time window.
         * The {@code numberOfAgeBuckets} defines how smoothly the time window moves forward.
         * For example, if the time window is 5 minutes and has 5 age buckets,
         * then it is moving forward every minute by one minute.
         * Default is {@link #DEFAULT_NUMBER_OF_AGE_BUCKETS}.
         */
        public Builder numberOfAgeBuckets(int ageBuckets) {
            if (ageBuckets <= 0) {
                throw new IllegalArgumentException("ageBuckets cannot be " + ageBuckets);
            }
            this.ageBuckets = ageBuckets;
            return this;
        }

        @Override
        protected MetricsProperties toProperties() {
            double[] quantiles = null;
            double[] quantileErrors = null;
            if (!this.quantiles.isEmpty()) {
                quantiles = new double[this.quantiles.size()];
                quantileErrors = new double[this.quantiles.size()];
                for (int i = 0; i < this.quantiles.size(); i++) {
                    quantiles[i] = this.quantiles.get(i).quantile;
                    quantileErrors[i] = this.quantiles.get(i).epsilon;
                }
            }
            return MetricsProperties.builder()
                    .exemplarsEnabled(exemplarsEnabled)
                    .summaryQuantiles(quantiles)
                    .summaryQuantileErrors(quantileErrors)
                    .summaryNumberOfAgeBuckets(ageBuckets)
                    .summaryMaxAgeSeconds(maxAgeSeconds)
                    .build();
        }

        /**
         * Default properties for summary metrics.
         */
        @Override
        public MetricsProperties getDefaultProperties() {
            return MetricsProperties.builder()
                    .exemplarsEnabled(true)
                    .summaryQuantiles()
                    .summaryNumberOfAgeBuckets(DEFAULT_NUMBER_OF_AGE_BUCKETS)
                    .summaryMaxAgeSeconds(DEFAULT_MAX_AGE_SECONDS)
                    .build();
        }

        @Override
        public Summary build() {
            return new Summary(this, properties);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }

            if (maxAgeSeconds <= 0) {
                throw new IllegalArgumentException("maxAgeSeconds cannot be " + maxAgeSeconds);
            }
            this.maxAgeSeconds = maxAgeSeconds;
            return this;
        
<methods>public void clear() ,public io.prometheus.metrics.model.snapshots.MetricSnapshot collect() ,public transient void initLabelValues(java.lang.String[]) ,public transient io.prometheus.metrics.core.datapoints.DistributionDataPoint labelValues(java.lang.String[]) ,public transient void remove(java.lang.String[]) <variables>private final ConcurrentHashMap<List<java.lang.String>,io.prometheus.metrics.core.metrics.Summary.DataPoint> data,private volatile io.prometheus.metrics.core.metrics.Summary.DataPoint noLabels