From e678fdd1e2ca3332dc0d74e08d565c7c1c6beace Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Fri, 12 Sep 2025 11:05:13 -0400 Subject: [PATCH 01/23] Add histogram test cases --- share/testdata/histograms/go.mod | 11 + share/testdata/histograms/go.sum | 10 + share/testdata/histograms/histograms.go | 728 +++++++++++++++++++ share/testdata/histograms/histograms_test.go | 347 +++++++++ 4 files changed, 1096 insertions(+) create mode 100644 share/testdata/histograms/go.mod create mode 100644 share/testdata/histograms/go.sum create mode 100644 share/testdata/histograms/histograms.go create mode 100644 share/testdata/histograms/histograms_test.go diff --git a/share/testdata/histograms/go.mod b/share/testdata/histograms/go.mod new file mode 100644 index 0000000000000..740171b6172fe --- /dev/null +++ b/share/testdata/histograms/go.mod @@ -0,0 +1,11 @@ +module github.com/amazon-contributing/opentelemetry-collector-contrib/share/testdata/histograms + +go 1.25.0 + +require github.com/stretchr/testify v1.11.1 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/share/testdata/histograms/go.sum b/share/testdata/histograms/go.sum new file mode 100644 index 0000000000000..c4c1710c475c1 --- /dev/null +++ b/share/testdata/histograms/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/share/testdata/histograms/histograms.go b/share/testdata/histograms/histograms.go new file mode 100644 index 0000000000000..55290048d8987 --- /dev/null +++ b/share/testdata/histograms/histograms.go @@ -0,0 +1,728 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package histograms + +import ( + "math" +) + +type HistogramTestCase struct { + Name string + Input HistogramInput + Expected ExpectedMetrics +} + +type HistogramInput struct { + Count uint64 + Sum float64 + Min *float64 + Max *float64 + Boundaries []float64 + Counts []uint64 + Attributes map[string]string +} + +type PercentileRange struct { + Low float64 + High float64 +} +type ExpectedMetrics struct { + Count uint64 + Sum float64 + Average float64 + Min *float64 + Max *float64 + PercentileRanges map[float64]PercentileRange +} + +func TestCases() []HistogramTestCase { + + // Create large bucket arrays with 11 items per bucket + boundaries125 := make([]float64, 125) + counts125 := make([]uint64, 126) + for i := 0; i < 125; i++ { + boundaries125[i] = float64(i+1) * 10 + counts125[i] = 11 + } + counts125[125] = 11 + + boundaries175 := make([]float64, 175) + counts175 := make([]uint64, 176) + for i := 0; i < 175; i++ { + boundaries175[i] = float64(i+1) * 10 + counts175[i] = 11 + } + counts175[175] = 11 + + boundaries225 := make([]float64, 225) + counts225 := make([]uint64, 226) + for i := 0; i < 225; i++ { + boundaries225[i] = float64(i+1) * 10 + counts225[i] = 11 + } + counts225[225] = 11 + + boundaries325 := make([]float64, 325) + counts325 := make([]uint64, 326) + for i := 0; i < 325; i++ { + boundaries325[i] = float64(i+1) * 10 + counts325[i] = 11 + } + counts325[325] = 11 + + return []HistogramTestCase{ + { + Name: "Basic Histogram", + Input: HistogramInput{ + Count: 101, + Sum: 6000, + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Counts: []uint64{21, 31, 25, 15, 7, 2}, + Attributes: map[string]string{"service.name": "payment-service"}, + }, + Expected: ExpectedMetrics{ + Count: 101, + Sum: 6000, + Average: 59.41, + Min: ptr(10.0), + Max: ptr(200.0), + PercentileRanges: map[float64]PercentileRange{ + 0.01: {Low: 10.0, High: 25.0}, + 0.1: {Low: 10.0, High: 25.0}, + 0.25: {Low: 25.0, High: 50.0}, + 0.5: {Low: 25.0, High: 50.0}, + 0.75: {Low: 50.0, High: 75.0}, + 0.9: {Low: 75.0, High: 100.0}, + 0.99: {Low: 150.0, High: 200.0}, + }, + }, + }, + { + Name: "Single Bucket", + Input: HistogramInput{ + Count: 51, + Sum: 1000, + Min: ptr(5.0), + Max: ptr(75.0), + Boundaries: []float64{}, + Counts: []uint64{51}, + Attributes: map[string]string{"service.name": "auth-service"}, + }, + Expected: ExpectedMetrics{ + Count: 51, + Sum: 1000, + Average: 19.61, + Min: ptr(5.0), + Max: ptr(75.0), + PercentileRanges: map[float64]PercentileRange{ + 0.01: {Low: 5.0, High: 75.0}, + 0.1: {Low: 5.0, High: 75.0}, + 0.25: {Low: 5.0, High: 75.0}, + 0.5: {Low: 5.0, High: 75.0}, + 0.75: {Low: 5.0, High: 75.0}, + 0.9: {Low: 5.0, High: 75.0}, + 0.99: {Low: 5.0, High: 75.0}, + }, + }, + }, + { + Name: "Two Buckets", + Input: HistogramInput{ + Count: 31, + Sum: 150, + Min: ptr(1.0), + Max: ptr(10.0), + Boundaries: []float64{5}, + Counts: []uint64{21, 10}, + Attributes: map[string]string{"service.name": "database"}, + }, + Expected: ExpectedMetrics{ + Count: 31, + Sum: 150, + Average: 4.84, + Min: ptr(1.0), + Max: ptr(10.0), + PercentileRanges: map[float64]PercentileRange{ + 0.01: {Low: 1.0, High: 5.0}, + 0.1: {Low: 1.0, High: 5.0}, + 0.25: {Low: 1.0, High: 5.0}, + 0.5: {Low: 1.0, High: 5.0}, + 0.75: {Low: 5.0, High: 10.0}, + 0.9: {Low: 5.0, High: 10.0}, + 0.99: {Low: 5.0, High: 10.0}, + }, + }, + }, + { + Name: "Zero Counts and Sparse Data", + Input: HistogramInput{ + Count: 101, + Sum: 25000, + Min: ptr(0.0), + Max: ptr(1500.0), + Boundaries: []float64{10, 50, 100, 500, 1000}, + Counts: []uint64{51, 0, 0, 39, 0, 11}, + Attributes: map[string]string{"service.name": "cache-service"}, + }, + Expected: ExpectedMetrics{ + Count: 101, + Sum: 25000, + Average: 247.52, + Min: ptr(0.0), + Max: ptr(1500.0), + PercentileRanges: map[float64]PercentileRange{ + 0.01: {Low: 0.0, High: 10.0}, + 0.1: {Low: 0.0, High: 10.0}, + 0.25: {Low: 0.0, High: 10.0}, + 0.5: {Low: 0.0, High: 10.0}, + 0.75: {Low: 100.0, High: 500.0}, + 0.9: {Low: 1000.0, High: 1500.0}, + 0.99: {Low: 1000.0, High: 1500.0}, + }, + }, + }, + { + Name: "Large Numbers", + Input: HistogramInput{ + Count: 1001, + Sum: 100000000000, + Min: ptr(100000.0), + Max: ptr(1000000000.0), + Boundaries: []float64{1000000, 10000000, 50000000, 100000000, 500000000}, + Counts: []uint64{201, 301, 249, 150, 50, 50}, + Attributes: map[string]string{"service.name": "batch-processor"}, + }, + Expected: ExpectedMetrics{ + Count: 1001, + Sum: 100000000000, + Average: 99900099.90, + Min: ptr(100000.0), + Max: ptr(1000000000.0), + PercentileRanges: map[float64]PercentileRange{ + 0.01: {Low: 100000.0, High: 1000000.0}, + 0.1: {Low: 100000.0, High: 1000000.0}, + 0.25: {Low: 1000000.0, High: 10000000.0}, + 0.5: {Low: 1000000.0, High: 10000000.0}, + 0.75: {Low: 10000000.0, High: 50000000.0}, + 0.9: {Low: 50000000.0, High: 100000000.0}, + 0.99: {Low: 500000000.0, High: 1000000000.0}, + }, + }, + }, + { + Name: "Many Buckets", + Input: HistogramInput{ + Count: 1124, + Sum: 350000, + Min: ptr(0.5), + Max: ptr(1100.0), + Boundaries: []float64{1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}, + Counts: []uint64{51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 53}, + Attributes: map[string]string{"service.name": "detailed-metrics"}, + }, + Expected: ExpectedMetrics{ + Count: 1111, + Sum: 350000, + Average: 315.03, + Min: ptr(0.5), + Max: ptr(1100.0), + PercentileRanges: map[float64]PercentileRange{ + 0.01: {Low: 0.5, High: 1.0}, + 0.1: {Low: 5.0, High: 10.0}, + 0.25: {Low: 30.0, High: 40.0}, + 0.5: {Low: 90.0, High: 100.0}, + 0.75: {Low: 500.0, High: 600.0}, + 0.9: {Low: 800.0, High: 900.0}, + 0.99: {Low: 1000.0, High: 1100.0}, + }, + }, + }, + { + Name: "Very Small Numbers", + Input: HistogramInput{ + Count: 101, + Sum: 0.00015, + Min: ptr(0.00000001), + Max: ptr(0.000006), + Boundaries: []float64{0.0000001, 0.000001, 0.000002, 0.000003, 0.000004, 0.000005}, + Counts: []uint64{11, 21, 29, 20, 15, 4, 1}, + Attributes: map[string]string{"service.name": "micro-timing"}, + }, + Expected: ExpectedMetrics{ + Count: 101, + Sum: 0.00015, + Average: 0.00000149, + Min: ptr(0.00000001), + Max: ptr(0.000006), + PercentileRanges: map[float64]PercentileRange{ + 0.01: {Low: 0.00000001, High: 0.0000001}, + 0.1: {Low: 0.00000001, High: 0.0000001}, + 0.25: {Low: 0.0000001, High: 0.000001}, + 0.5: {Low: 0.000001, High: 0.000002}, + 0.75: {Low: 0.000002, High: 0.000003}, + 0.9: {Low: 0.000003, High: 0.000004}, + 0.99: {Low: 0.000004, High: 0.000005}, + }, + }, + }, + { + Name: "Only Negative Boundaries", + Input: HistogramInput{ + Count: 101, + Sum: -10000, + Min: ptr(-200.0), + Max: ptr(-10.0), + Boundaries: []float64{-150, -100, -75, -50, -25}, + Counts: []uint64{21, 31, 25, 15, 7, 2}, + Attributes: map[string]string{"service.name": "negative-service"}, + }, + Expected: ExpectedMetrics{ + Count: 101, + Sum: -6000, + Average: -59.41, + Min: ptr(-200.0), + Max: ptr(-10.0), + // Can't get percentiles for negatives + PercentileRanges: map[float64]PercentileRange{}, + }, + }, + { + Name: "Negative and Positive Boundaries", + Input: HistogramInput{ + Count: 106, + Sum: 0, + Min: ptr(-50.0), + Max: ptr(50.0), + Boundaries: []float64{-30, -10, 10, 30}, + Counts: []uint64{25, 26, 5, 25, 25}, + Attributes: map[string]string{"service.name": "temperature-service"}, + }, + Expected: ExpectedMetrics{ + Count: 101, + Sum: 0, + Average: 0.0, + Min: ptr(-50.0), + Max: ptr(50.0), + // Can't get percentiles for negatives + PercentileRanges: map[float64]PercentileRange{}, + }, + }, + + { + Name: "Positive boundaries but implied Negative Values", + Input: HistogramInput{ + Count: 101, + Sum: 200, + Min: ptr(-100.0), + Max: ptr(60.0), + Boundaries: []float64{0, 10, 20, 30, 40, 50}, + Counts: []uint64{61, 10, 10, 10, 5, 4, 1}, + Attributes: map[string]string{"service.name": "temperature-service"}, + }, + Expected: ExpectedMetrics{ + Count: 101, + Sum: -3000, + Average: -29.70, + Min: ptr(-100.0), + Max: ptr(60.0), + // Can't get percentiles for negatives + PercentileRanges: map[float64]PercentileRange{}, + }, + }, + { + Name: "First bucket boundary equals minimum", + Input: HistogramInput{ + Count: 100, + Sum: 8000, + Min: ptr(10.0), + Max: ptr(160.0), + Boundaries: []float64{10, 75, 100, 150}, + Counts: []uint64{20, 30, 25, 15, 10}, + Attributes: map[string]string{"service.name": "invalid-max-bucket"}, + }, + Expected: ExpectedMetrics{ + Count: 100, + Sum: 10000, + Average: 1000, + Min: ptr(10.0), + Max: ptr(160.0), + PercentileRanges: map[float64]PercentileRange{ + 0.1: {Low: 10.0, High: 10.0}, + 0.25: {Low: 10.0, High: 75.0}, + 0.5: {Low: 75.0, High: 100.0}, + 0.75: {Low: 100.0, High: 150.0}, + 0.9: {Low: 150.0, High: 160.0}, + }, + }, + }, + { + Name: "No Min or Max", + Input: HistogramInput{ + Count: 75, + Sum: 3500, + Min: nil, + Max: nil, + Boundaries: []float64{10, 50, 100, 200}, + Counts: []uint64{15, 21, 24, 10, 5}, + Attributes: map[string]string{"service.name": "web-service"}, + }, + Expected: ExpectedMetrics{ + Count: 75, + Sum: 3500, + Average: 46.67, + Min: nil, + Max: nil, + PercentileRanges: map[float64]PercentileRange{ + 0.1: {Low: math.Inf(-1), High: 10.0}, + 0.25: {Low: 10.0, High: 50.0}, + 0.5: {Low: 50.0, High: 100.0}, + 0.75: {Low: 50.0, High: 100.0}, + 0.9: {Low: 100.0, High: 200.0}, + }, + }, + }, + { + Name: "Only Max Defined", + Input: HistogramInput{ + Count: 101, + Sum: 17500, + Min: nil, + Max: ptr(750.0), + Boundaries: []float64{100, 200, 300, 400, 500}, + Counts: []uint64{21, 31, 24, 15, 5, 5}, + Attributes: map[string]string{"service.name": "api-gateway"}, + }, + Expected: ExpectedMetrics{ + Count: 101, + Sum: 17500, + Average: 173.27, + Min: nil, + Max: ptr(750.0), + PercentileRanges: map[float64]PercentileRange{ + 0.1: {Low: math.Inf(-1), High: 100.0}, + 0.25: {Low: 100.0, High: 200.0}, + 0.5: {Low: 100.0, High: 200.0}, + 0.75: {Low: 200.0, High: 300.0}, + 0.9: {Low: 300.0, High: 400.0}, + }, + }, + }, + { + Name: "Only Min Defined", + Input: HistogramInput{ + Count: 51, + Sum: 4000, + Min: ptr(25.0), + Max: nil, + Boundaries: []float64{50, 100, 150}, + Counts: []uint64{11, 21, 14, 5}, + Attributes: map[string]string{"service.name": "queue-service"}, + }, + Expected: ExpectedMetrics{ + Count: 51, + Sum: 4000, + Average: 78.43, + Min: ptr(25.0), + Max: nil, + PercentileRanges: map[float64]PercentileRange{ + 0.1: {Low: 25.0, High: 50.0}, + 0.25: {Low: 50.0, High: 100.0}, + 0.5: {Low: 50.0, High: 100.0}, + 0.75: {Low: 100.0, High: 150.0}, + 0.9: {Low: 100.0, High: 150.0}, + }, + }, + }, + { + Name: "No Min/Max with Single Value", + Input: HistogramInput{ + Count: 1, + Sum: 100, + Min: nil, + Max: nil, + Boundaries: []float64{50, 150}, + Counts: []uint64{0, 1, 0}, + Attributes: map[string]string{"service.name": "singleton-service"}, + }, + Expected: ExpectedMetrics{ + Count: 1, + Sum: 100, + Average: 100.0, + Min: nil, + Max: nil, + PercentileRanges: map[float64]PercentileRange{ + 0.1: {Low: 50.0, High: 150.0}, + 0.25: {Low: 50.0, High: 150.0}, + 0.5: {Low: 50.0, High: 150.0}, + 0.75: {Low: 50.0, High: 150.0}, + 0.9: {Low: 50.0, High: 150.0}, + }, + }, + }, + { + Name: "Unbounded Histogram", + Input: HistogramInput{ + Count: 75, + Sum: 3500, + Min: nil, + Max: nil, + Boundaries: []float64{}, + Counts: []uint64{}, + Attributes: map[string]string{"service.name": "unbounded-service"}, + }, + Expected: ExpectedMetrics{ + Count: 75, + Sum: 3500, + Average: 46.67, + Min: nil, + Max: nil, + PercentileRanges: map[float64]PercentileRange{ + 0.1: {Low: math.Inf(-1), High: math.Inf(1)}, + 0.25: {Low: math.Inf(-1), High: math.Inf(1)}, + 0.5: {Low: math.Inf(-1), High: math.Inf(1)}, + 0.75: {Low: math.Inf(-1), High: math.Inf(1)}, + 0.9: {Low: math.Inf(-1), High: math.Inf(1)}, + }, + }, + }, + // >100 buckets will be used for testing request splitting in PMD path + { + Name: "126 Buckets", + Input: HistogramInput{ + Count: 1386, // 126 buckets * 11 items each + Sum: 870555, + Min: ptr(5.0), + Max: ptr(1300.0), + Boundaries: boundaries125, + Counts: counts125, + Attributes: map[string]string{"service.name": "many-buckets-125"}, + }, + Expected: ExpectedMetrics{ + Count: 1386, + Sum: 870555, + Average: 573.14, + Min: ptr(5.0), + Max: ptr(1300.0), + PercentileRanges: map[float64]PercentileRange{ + 0.1: {Low: 120.0, High: 130.0}, + 0.25: {Low: 310.0, High: 320.0}, + 0.5: {Low: 630.0, High: 640.0}, + 0.75: {Low: 940.0, High: 950.0}, + 0.9: {Low: 1130.0, High: 1140.0}, + }, + }, + }, + // >150 buckets will be used for testing request splitting in EMF path + { + Name: "176 Buckets", + Input: HistogramInput{ + Count: 1936, // 176 buckets * 11 items each + Sum: 1697000, + Min: ptr(5.0), + Max: ptr(1800.0), + Boundaries: boundaries175, + Counts: counts175, + Attributes: map[string]string{"service.name": "many-buckets-175"}, + }, + Expected: ExpectedMetrics{ + Count: 1936, + Sum: 1557000, + Average: 804.23, + Min: ptr(5.0), + Max: ptr(1800.0), + PercentileRanges: map[float64]PercentileRange{ + 0.1: {Low: 170.0, High: 180.0}, + 0.25: {Low: 440.0, High: 450.0}, + 0.5: {Low: 880.0, High: 890.0}, + 0.75: {Low: 1320.0, High: 1330.0}, + 0.9: {Low: 1580.0, High: 1590.0}, + }, + }, + }, + // PMD should split into 3 requests + // EMF should split into 2 requests + { + Name: "225 Buckets", + Input: HistogramInput{ + Count: 2486, // 226 buckets * 11 items each + Sum: 2803750, + Min: ptr(5.0), + Max: ptr(2300.0), + Boundaries: boundaries225, + Counts: counts225, + Attributes: map[string]string{"service.name": "many-buckets-225"}, + }, + Expected: ExpectedMetrics{ + Count: 2486, + Sum: 2803750, + Average: 1027.25, + Min: ptr(5.0), + Max: ptr(2300.0), + PercentileRanges: map[float64]PercentileRange{ + 0.1: {Low: 220.0, High: 230.0}, + 0.25: {Low: 560.0, High: 570.0}, + 0.5: {Low: 1130.0, High: 1140.0}, + 0.75: {Low: 1690.0, High: 1700.0}, + 0.9: {Low: 2030.0, High: 2040.0}, + }, + }, + }, + // PMD should split into 4 requests + // EMF should split into 3 requests + { + Name: "325 Buckets", + Input: HistogramInput{ + Count: 3586, // 326 buckets * 11 items each + Sum: 5830500, + Min: ptr(5.0), + Max: ptr(3300.0), + Boundaries: boundaries325, + Counts: counts325, + Attributes: map[string]string{"service.name": "many-buckets-325"}, + }, + Expected: ExpectedMetrics{ + Count: 3586, + Sum: 5830500, + Average: 1486.47, + Min: ptr(5.0), + Max: ptr(3300.0), + PercentileRanges: map[float64]PercentileRange{ + 0.1: {Low: 320.0, High: 330.0}, + 0.25: {Low: 810.0, High: 820.0}, + 0.5: {Low: 1630.0, High: 1640.0}, + 0.75: {Low: 2440.0, High: 2450.0}, + 0.9: {Low: 2930.0, High: 2940.0}, + }, + }, + }, + } +} + +func InvalidTestCases() []HistogramTestCase { + return []HistogramTestCase{ + { + Name: "Boundaries Not Ascending", + Input: HistogramInput{ + Count: 100, + Sum: 5000, + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 40, 100, 150}, // 40 < 50 + Counts: []uint64{20, 30, 25, 15, 8, 2}, + Attributes: map[string]string{"service.name": "invalid-boundaries"}, + }, + Expected: ExpectedMetrics{}, + }, + { + Name: "Counts Length Mismatch", + Input: HistogramInput{ + Count: 100, + Sum: 5000, + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100}, + Counts: []uint64{20, 30, 25, 15, 8, 2}, // Should be 5 counts for 4 boundaries + Attributes: map[string]string{"service.name": "wrong-counts"}, + }, + Expected: ExpectedMetrics{}, + }, + { + Name: "Total Count Mismatch", + Input: HistogramInput{ + Count: 90, // Doesn't match sum of counts (100) + Sum: 5000, + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Counts: []uint64{20, 30, 25, 15, 8, 2}, + Attributes: map[string]string{"service.name": "count-mismatch"}, + }, + Expected: ExpectedMetrics{}, + }, + { + Name: "Min Greater Than First Boundary", + Input: HistogramInput{ + Count: 100, + Sum: 5000, + Min: ptr(30.0), // Greater than first boundary (25) + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Counts: []uint64{20, 30, 25, 15, 8, 2}, // Has counts in first bucket + Attributes: map[string]string{"service.name": "invalid-min"}, + }, + Expected: ExpectedMetrics{}, + }, + { + Name: "Max Less Than Last Boundary", + Input: HistogramInput{ + Count: 100, + Sum: 5000, + Min: ptr(10.0), + Max: ptr(140.0), // Less than last boundary (150) + Boundaries: []float64{25, 50, 75, 100, 150}, + Counts: []uint64{20, 30, 25, 15, 8, 2}, // Has counts in overflow bucket + Attributes: map[string]string{"service.name": "invalid-max"}, + }, + Expected: ExpectedMetrics{}, + }, + { + Name: "Sum Too Small", + Input: HistogramInput{ + Count: 100, + Sum: 100, // Too small given the boundaries and counts + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Counts: []uint64{20, 30, 25, 15, 8, 2}, + Attributes: map[string]string{"service.name": "small-sum"}, + }, + Expected: ExpectedMetrics{}, + }, + { + Name: "Sum Too Large", + Input: HistogramInput{ + Count: 100, + Sum: 1000000, // Too large given the boundaries and counts + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Counts: []uint64{20, 30, 25, 15, 8, 2}, + Attributes: map[string]string{"service.name": "large-sum"}, + }, + Expected: ExpectedMetrics{}, + }, + { + Name: "Min in Second Bucket But Sum Too Low", + Input: HistogramInput{ + Count: 100, + Sum: 2000, // This sum is too low given min is in second bucket + Min: ptr(60.0), // Min falls in second bucket (50,75] + Max: ptr(200.0), + Boundaries: []float64{50, 75, 100, 150}, + Counts: []uint64{20, 30, 25, 15, 10}, // 30 values must be at least 60 each in second bucket + Attributes: map[string]string{"service.name": "invalid-min-bucket"}, + }, + Expected: ExpectedMetrics{}, + }, + { + Name: "Max in Second-to-Last Bucket But Sum Too High", + Input: HistogramInput{ + Count: 100, + Sum: 10000, // This sum is too high given max is in second-to-last bucket + Min: ptr(10.0), + Max: ptr(90.0), // Max falls in second-to-last bucket (75,100] + Boundaries: []float64{50, 75, 100, 150}, + Counts: []uint64{20, 30, 25, 15, 10}, // No value can exceed 90 + Attributes: map[string]string{"service.name": "invalid-max-bucket"}, + }, + Expected: ExpectedMetrics{}, + }, + } +} + +func ptr(f float64) *float64 { + return &f +} diff --git a/share/testdata/histograms/histograms_test.go b/share/testdata/histograms/histograms_test.go new file mode 100644 index 0000000000000..f6ea357921224 --- /dev/null +++ b/share/testdata/histograms/histograms_test.go @@ -0,0 +1,347 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package histograms + +import ( + "fmt" + "math" + "regexp" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHistogramFeasibility(t *testing.T) { + testCases := TestCases() + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + feasible, reason := checkFeasibility(tc.Input) + assert.True(t, feasible, reason) + + // check that the test case percentile ranges are valid + for percentile, expectedRange := range tc.Expected.PercentileRanges { + calculatedLow, calculatedHigh := calculatePercentileRange(tc.Input, percentile) + assert.Equal(t, expectedRange.Low, calculatedLow, "calculated low does not match expected low for percentile %v", percentile) + assert.Equal(t, expectedRange.High, calculatedHigh, "calculated high does not match expected high for percentile %v", percentile) + } + + assertOptionalFloat(t, "min", tc.Expected.Min, tc.Input.Min) + assertOptionalFloat(t, "max", tc.Expected.Max, tc.Input.Max) + }) + } +} + +func TestInvalidHistogramFeasibility(t *testing.T) { + invalidTestCases := InvalidTestCases() + + for _, tc := range invalidTestCases { + t.Run(tc.Name, func(t *testing.T) { + feasible, reason := checkFeasibility(tc.Input) + assert.False(t, feasible, reason) + }) + } +} + +func TestVisualizeHistograms(t *testing.T) { + // comment the next line to visualize the input histograms + t.Skip("Skip visualization test") + testCases := TestCases() + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + // The large bucket tests are just too big to output + if matched, _ := regexp.MatchString("\\d\\d\\d Buckets", tc.Name); matched { + return + } + visualizeHistogramWithPercentiles(tc.Input) + }) + } +} + +func checkFeasibility(hi HistogramInput) (bool, string) { + + // Special case: empty histogram is valid + if len(hi.Boundaries) == 0 && len(hi.Counts) == 0 { + return true, "" + } + + // Check counts length matches boundaries + 1 + if len(hi.Counts) != len(hi.Boundaries)+1 { + return false, "Can't have counts without boundaries" + } + + if hi.Max != nil && hi.Min != nil && *hi.Min > *hi.Max { + return false, fmt.Sprintf("min %f is greater than max %f", *hi.Min, *hi.Max) + } + + // Rest of checks only apply if we have boundaries/counts + if len(hi.Boundaries) > 0 || len(hi.Counts) > 0 { + // Check boundaries are in ascending order + for i := 1; i < len(hi.Boundaries); i++ { + if hi.Boundaries[i] <= hi.Boundaries[i-1] { + return false, fmt.Sprintf("boundaries not in ascending order: %v <= %v", + hi.Boundaries[i], hi.Boundaries[i-1]) + } + } + + // Check counts array length + if len(hi.Counts) != len(hi.Boundaries)+1 { + return false, fmt.Sprintf("counts length (%d) should be boundaries length (%d) + 1", + len(hi.Counts), len(hi.Boundaries)) + } + + // Verify total count matches sum of bucket counts + var totalCount uint64 + for _, count := range hi.Counts { + totalCount += count + } + if totalCount != hi.Count { + return false, fmt.Sprintf("sum of counts (%d) doesn't match total count (%d)", + totalCount, hi.Count) + } + + // Check min/max feasibility if defined + if hi.Min != nil { + // If there are boundaries, first bucket must have counts > 0 only if min <= first boundary + if len(hi.Boundaries) > 0 && hi.Counts[0] > 0 && *hi.Min > hi.Boundaries[0] { + return false, fmt.Sprintf("min (%v) > first boundary (%v) but first bucket has counts", + *hi.Min, hi.Boundaries[0]) + } + } + + if hi.Max != nil { + // If there are boundaries, last bucket must have counts > 0 only if max > last boundary + if len(hi.Boundaries) > 0 && hi.Counts[len(hi.Counts)-1] > 0 && + *hi.Max <= hi.Boundaries[len(hi.Boundaries)-1] { + return false, fmt.Sprintf("max (%v) <= last boundary (%v) but overflow bucket has counts", + *hi.Max, hi.Boundaries[len(hi.Boundaries)-1]) + } + } + + // Check sum feasibility + if len(hi.Boundaries) > 0 { + // Calculate minimum possible sum + minSum := float64(0) + if hi.Min != nil { + // Find which bucket the minimum value belongs to + minBucket := 0 + for i, bound := range hi.Boundaries { + if *hi.Min > bound { + minBucket = i + 1 + } + } + // Apply min value only from its containing bucket + for i := minBucket; i < len(hi.Counts); i++ { + if i == minBucket { + minSum += float64(hi.Counts[i]) * *hi.Min + } else { + minSum += float64(hi.Counts[i]) * hi.Boundaries[i-1] + } + } + } else { + // Without min, use lower bounds + for i := 1; i < len(hi.Counts); i++ { + minSum += float64(hi.Counts[i]) * hi.Boundaries[i-1] + } + } + + // Calculate maximum possible sum + maxSum := float64(0) + if hi.Max != nil { + // Find which bucket the maximum value belongs to + maxBucket := len(hi.Boundaries) // Default to overflow bucket + for i, bound := range hi.Boundaries { + if *hi.Max <= bound { + maxBucket = i + break + } + } + // Apply max value only up to its containing bucket + for i := 0; i < len(hi.Counts); i++ { + if i > maxBucket { + maxSum += float64(hi.Counts[i]) * *hi.Max + } else if i == len(hi.Boundaries) { + maxSum += float64(hi.Counts[i]) * *hi.Max + } else { + maxSum += float64(hi.Counts[i]) * hi.Boundaries[i] + } + } + } else { + // If no max defined, we can't verify upper bound + maxSum = math.Inf(1) + } + + if hi.Sum < minSum { + return false, fmt.Sprintf("sum (%v) is less than minimum possible sum (%v)", + hi.Sum, minSum) + } + if maxSum != math.Inf(1) && hi.Sum > maxSum { + return false, fmt.Sprintf("sum (%v) is greater than maximum possible sum (%v)", + hi.Sum, maxSum) + } + } + } + + return true, "" +} + +func calculatePercentileRange(hi HistogramInput, percentile float64) (float64, float64) { + if len(hi.Boundaries) == 0 { + // No buckets - use min/max if available + if hi.Min != nil && hi.Max != nil { + return *hi.Min, *hi.Max + } + return math.Inf(-1), math.Inf(1) + } + + percentilePosition := uint64(float64(hi.Count) * percentile) + var cumulativeCount uint64 + + // Find which bucket contains the percentile + for i, count := range hi.Counts { + cumulativeCount += count + if cumulativeCount > percentilePosition { + // Found the bucket containing the percentile + if i == 0 { + // First bucket: (-inf, bounds[0]] + if hi.Min != nil { + return *hi.Min, hi.Boundaries[0] + } + return math.Inf(-1), hi.Boundaries[0] + } else if i == len(hi.Boundaries) { + // Last bucket: (bounds[last], +inf) + if hi.Max != nil { + return hi.Boundaries[i-1], *hi.Max + } + return hi.Boundaries[i-1], math.Inf(1) + } else { + // Middle bucket: (bounds[i-1], bounds[i]] + return hi.Boundaries[i-1], hi.Boundaries[i] + } + } + } + return 0, 0 // Should never reach here for valid histograms +} + +func assertOptionalFloat(t *testing.T, name string, expected, actual *float64) { + if expected != nil { + assert.NotNil(t, actual, "Expected %s defined but not defined on input", name) + if actual != nil { + assert.Equal(t, expected, actual) + } + } else { + assert.Nil(t, actual, "Input %s defined but no %s is expected", name, name) + } +} + +func visualizeHistogramWithPercentiles(hi HistogramInput) { + fmt.Printf("\nHistogram Visualization with Percentiles\n") + fmt.Printf("Count: %d, Sum: %.2f\n", hi.Count, hi.Sum) + if hi.Min != nil { + fmt.Printf("Min: %.2f ", *hi.Min) + } + if hi.Max != nil { + fmt.Printf("Max: %.2f", *hi.Max) + } + fmt.Println() + + if len(hi.Boundaries) == 0 { + fmt.Println("No buckets defined") + return + } + + // Calculate cumulative counts for CDF + cumulativeCounts := make([]uint64, len(hi.Counts)) + var total uint64 + for i, count := range hi.Counts { + total += count + cumulativeCounts[i] = total + } + + // Find percentile positions + percentiles := []float64{0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99} + percentilePositions := make(map[float64]int) + for _, p := range percentiles { + pos := uint64(float64(hi.Count) * p) + for i, cumCount := range cumulativeCounts { + if cumCount > pos { + percentilePositions[p] = i + break + } + } + } + + maxCount := uint64(0) + for _, count := range hi.Counts { + if count > maxCount { + maxCount = count + } + } + + fmt.Println("\nHistogram:") + for i, count := range hi.Counts { + var bucketLabel string + if i == 0 { + if hi.Min != nil { + bucketLabel = fmt.Sprintf("(%.2f, %.1f]", *hi.Min, hi.Boundaries[0]) + } else { + bucketLabel = fmt.Sprintf("(-∞, %.1f]", hi.Boundaries[0]) + } + } else if i == len(hi.Boundaries) { + if hi.Max != nil { + bucketLabel = fmt.Sprintf("(%.1f, %.2f]", hi.Boundaries[i-1], *hi.Max) + } else { + bucketLabel = fmt.Sprintf("(%.1f, +∞)", hi.Boundaries[i-1]) + } + } else { + bucketLabel = fmt.Sprintf("(%.1f, %.1f]", hi.Boundaries[i-1], hi.Boundaries[i]) + } + + barLength := int(float64(count) / float64(maxCount) * 40) + bar := strings.Repeat("█", barLength) + + // Mark percentile buckets + percentileMarkers := "" + for _, p := range percentiles { + if percentilePositions[p] == i { + percentileMarkers += fmt.Sprintf(" P%.0f", p*100) + } + } + + fmt.Printf("%-30s %4d |%s%s\n", bucketLabel, count, bar, percentileMarkers) + } + + fmt.Println("\nCumulative Distribution (CDF):") + for i, cumCount := range cumulativeCounts { + var bucketLabel string + if i == 0 { + bucketLabel = fmt.Sprintf("≤ %.1f", hi.Boundaries[0]) + } else if i == len(hi.Boundaries) { + bucketLabel = "≤ +∞" + } else { + bucketLabel = fmt.Sprintf("≤ %.1f", hi.Boundaries[i]) + } + + cdfPercent := float64(cumCount) / float64(hi.Count) * 100 + cdfBarLength := int(cdfPercent / 100 * 40) + cdfBar := strings.Repeat("▓", cdfBarLength) + + // Add percentile lines + percentileLines := "" + for _, p := range percentiles { + if percentilePositions[p] == i { + percentileLines += fmt.Sprintf(" ──P%.0f", p*100) + } + } + + fmt.Printf("%-15s %6.1f%% |%s%s\n", bucketLabel, cdfPercent, cdfBar, percentileLines) + } + + // Show percentile ranges + fmt.Println("\nPercentile Ranges:") + for _, p := range percentiles { + low, high := calculatePercentileRange(hi, p) + fmt.Printf("P%.0f: [%.2f, %.2f]\n", p*100, low, high) + } +} From 0fa66079698e32353b4f166b46787fd8a6be2797 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Fri, 12 Sep 2025 11:05:36 -0400 Subject: [PATCH 02/23] Add prometheus metrics generator --- cmd/promgen/go.mod | 23 ++++ cmd/promgen/go.sum | 46 ++++++++ cmd/promgen/metrics.go | 241 +++++++++++++++++++++++++++++++++++++++++ cmd/promgen/promgen.go | 229 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 539 insertions(+) create mode 100644 cmd/promgen/go.mod create mode 100644 cmd/promgen/go.sum create mode 100644 cmd/promgen/metrics.go create mode 100644 cmd/promgen/promgen.go diff --git a/cmd/promgen/go.mod b/cmd/promgen/go.mod new file mode 100644 index 0000000000000..29f03d410bd43 --- /dev/null +++ b/cmd/promgen/go.mod @@ -0,0 +1,23 @@ +module github.com/amazon-contributing/opentelemetry-collector-contrib/cmd/promgen + +go 1.25.0 + +replace github.com/amazon-contributing/opentelemetry-collector-contrib/share/testdata/histograms => /local/home/dricross/workplace/classichistograms/opentelemetry-collector-contrib/share/testdata/histograms + +require ( + github.com/amazon-contributing/opentelemetry-collector-contrib/share/testdata/histograms v0.124.1 + github.com/prometheus/client_golang v1.23.2 + google.golang.org/protobuf v1.36.9 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.66.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/sys v0.35.0 // indirect +) diff --git a/cmd/promgen/go.sum b/cmd/promgen/go.sum new file mode 100644 index 0000000000000..ef6be1bb660cd --- /dev/null +++ b/cmd/promgen/go.sum @@ -0,0 +1,46 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cmd/promgen/metrics.go b/cmd/promgen/metrics.go new file mode 100644 index 0000000000000..ef577f8bd5945 --- /dev/null +++ b/cmd/promgen/metrics.go @@ -0,0 +1,241 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "math" + "math/rand" + "net/http" + "sync" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "google.golang.org/protobuf/proto" +) + +// MetricType represents supported Prometheus metric types +type MetricType string + +const ( + TypeUntyped MetricType = "untyped" + TypeCounter MetricType = "counter" + TypeGauge MetricType = "gauge" + TypeSummary MetricType = "summary" + TypeHistogram MetricType = "histogram" + TypeNativeHistogram MetricType = "native_histogram" +) + +// MetricDefinition defines a metric and its time series +type MetricDefinition struct { + Name string + Type MetricType + Help string + Labels []string + CreateCollector func() (prometheus.Collector, error) + Update func(prometheus.Collector, time.Time) error +} + +type Metric struct { + Collector prometheus.Collector + Update func(prometheus.Collector, time.Time) error +} + +// Generator manages the metrics and their generation +type Generator struct { + metrics map[string]Metric + registry *prometheus.Registry + mu sync.RWMutex + rand *rand.Rand +} + +var _ http.Handler = (*Generator)(nil) + +// NewGenerator creates a new metrics generator +func NewGenerator() *Generator { + return &Generator{ + metrics: make(map[string]Metric), + registry: prometheus.NewRegistry(), + rand: rand.New(rand.NewSource(0xFEEDBEEF)), // for deterministic results + } +} + +// AddMetric adds a new metric definition to the generator +func (g *Generator) AddMetric(def MetricDefinition) error { + g.mu.Lock() + defer g.mu.Unlock() + + var collector prometheus.Collector + var err error + if def.CreateCollector != nil { + collector, err = def.CreateCollector() + } else { + collector, err = g.defaultCollector(def) + } + if err != nil { + return fmt.Errorf("unable to create collector: %w", err) + } + + if err := g.registry.Register(collector); err != nil { + return fmt.Errorf("failed to register metric: %w", err) + } + + g.metrics[def.Name] = Metric{ + Collector: collector, + Update: def.Update, + } + return nil +} + +// UpdateMetrics updates metric values based on the current timestamp +func (g *Generator) UpdateMetrics(timestamp time.Time) error { + g.mu.Lock() + defer g.mu.Unlock() + + for name, m := range g.metrics { + if m.Update != nil { + if err := m.Update(m.Collector, timestamp); err != nil { + return fmt.Errorf("failed to update metric %s: %w", name, err) + } + } + } + + return nil +} + +// ServeHTTP implements http.Handler +func (g *Generator) ServeHTTP(w http.ResponseWriter, r *http.Request) { + format := r.Header.Get("Accept") + log.Printf("receiver %s request\n", format) + switch format { + case "application/json": + g.serveJSON(w, r) + case "application/vnd.google.protobuf": + g.serveProtobuf(w, r) + default: + promhttp.HandlerFor(g.registry, promhttp.HandlerOpts{}).ServeHTTP(w, r) + } +} + +// serveJSON serves metrics in JSON format +func (g *Generator) serveJSON(w http.ResponseWriter, r *http.Request) { + metrics, err := g.registry.Gather() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(metrics) +} + +// serveProtobuf serves metrics in Protobuf format +func (g *Generator) serveProtobuf(w http.ResponseWriter, r *http.Request) { + metrics, err := g.registry.Gather() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + for _, metric := range metrics { + data, err := proto.Marshal(metric) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/vnd.google.protobuf") + w.Write(data) + } +} + +func (g *Generator) defaultCollector(def MetricDefinition) (prometheus.Collector, error) { + switch def.Type { + case TypeCounter: + return prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: def.Name, + Help: def.Help, + }, + def.Labels, + ), nil + + case TypeGauge: + return prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: def.Name, + Help: def.Help, + }, + def.Labels, + ), nil + + case TypeHistogram: + return prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: def.Name, + Help: def.Help, + }, + def.Labels, + ), nil + + case TypeSummary: + return prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Name: def.Name, + Help: def.Help, + Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, + }, + def.Labels, + ), nil + + case TypeNativeHistogram: + return prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: def.Name, + Help: def.Help, + NativeHistogramBucketFactor: 1.1, + NativeHistogramZeroThreshold: 1e-6, + }, + def.Labels, + ), nil + + default: + return nil, fmt.Errorf("unsupported metric type: %s", def.Type) + } +} + +// GammaRandom generates a random number from a Gamma distribution +// shape (k) and scale (theta) are the parameters +func GammaRandom(rand *rand.Rand, shape, scale float64) float64 { + // Implementation of Marsaglia and Tsang's method + if shape < 1 { + // Use transformation for shape < 1 + return GammaRandom(rand, shape+1, scale) * math.Pow(rand.Float64(), 1.0/shape) + } + + d := shape - 1.0/3.0 + c := 1.0 / math.Sqrt(9.0*d) + + for { + x := 0.0 + v := 0.0 + for { + x = rand.NormFloat64() + v = 1.0 + c*x + if v > 0 { + break + } + } + + v = v * v * v + u := rand.Float64() + + if u < 1.0-0.331*math.Pow(x, 4) { + return d * v * scale + } + + if math.Log(u) < 0.5*x*x+d*(1.0-v+math.Log(v)) { + return d * v * scale + } + } +} diff --git a/cmd/promgen/promgen.go b/cmd/promgen/promgen.go new file mode 100644 index 0000000000000..bf5452d3cec6d --- /dev/null +++ b/cmd/promgen/promgen.go @@ -0,0 +1,229 @@ +package main + +import ( + "log" + "maps" + "math" + "net/http" + "slices" + "strings" + "time" + + "github.com/amazon-contributing/opentelemetry-collector-contrib/share/testdata/histograms" + "github.com/prometheus/client_golang/prometheus" +) + +const updatePeriod = time.Second + +func main() { + + start := time.Now() + generator := NewGenerator() + + monotonicCounter := MetricDefinition{ + Name: "monotonic_counter", + Type: TypeCounter, + Help: "A counter that increases forever", + Update: func(collector prometheus.Collector, timestamp time.Time) error { + counter, err := collector.(*prometheus.CounterVec).GetMetricWith(prometheus.Labels{}) + if err != nil { + return err + } + counter.Inc() + return nil + }, + } + + if err := generator.AddMetric(monotonicCounter); err != nil { + log.Fatalf("unable to add metric: %v", err) + } + + sinusoidalGauge := MetricDefinition{ + Name: "sinusoidal_gauge", + Type: TypeGauge, + Help: "A gauge that oscillates between -1 and 1", + Update: func(collector prometheus.Collector, timestamp time.Time) error { + gauge, err := collector.(*prometheus.GaugeVec).GetMetricWith(prometheus.Labels{}) + if err != nil { + return err + } + newVal := math.Sin(2 * math.Pi * float64(timestamp.Unix()) / 20) + gauge.Set(newVal) + return nil + }, + } + + if err := generator.AddMetric(sinusoidalGauge); err != nil { + log.Fatalf("unable to add metric: %v", err) + } + + gammaHistogram := MetricDefinition{ + Name: "gamma_histogram", + Type: TypeHistogram, + Help: "A histogram whose values follow a gamma distribution", + Update: func(collector prometheus.Collector, timestamp time.Time) error { + histogram, err := collector.(*prometheus.HistogramVec).GetMetricWith(prometheus.Labels{}) + if err != nil { + return err + } + numObservations := generator.rand.Int() % 10 + for range numObservations { + histogram.Observe(GammaRandom(generator.rand, 2.0, 2.0)) + } + return nil + }, + } + + if err := generator.AddMetric(gammaHistogram); err != nil { + log.Fatalf("unable to add metric: %v", err) + } + + exponentialSummary := MetricDefinition{ + Name: "exponential_summary", + Type: TypeSummary, + Help: "A summary whose values follow an exponential distribution", + Update: func(collector prometheus.Collector, timestamp time.Time) error { + summary, err := collector.(*prometheus.SummaryVec).GetMetricWith(prometheus.Labels{}) + if err != nil { + return err + } + numObservations := generator.rand.Int() % 10 + for range numObservations { + summary.Observe(generator.rand.ExpFloat64()) + } + return nil + }, + } + + if err := generator.AddMetric(exponentialSummary); err != nil { + log.Fatalf("unable to add metric: %v", err) + } + + gammaNativeHistogram := MetricDefinition{ + Name: "gamma_native_histogram", + Type: TypeNativeHistogram, + Help: "A native histogram whose values follow a gamma distribution", + Update: func(collector prometheus.Collector, timestamp time.Time) error { + histogram, err := collector.(*prometheus.HistogramVec).GetMetricWith(prometheus.Labels{}) + if err != nil { + return err + } + numObservations := generator.rand.Int() % 10 + for range numObservations { + histogram.Observe(GammaRandom(generator.rand, 2.0, 2.0)) + } + return nil + }, + } + + if err := generator.AddMetric(gammaNativeHistogram); err != nil { + log.Fatalf("unable to add metric: %v", err) + } + + testCases := histograms.TestCases() + for _, tc := range testCases { + tName := "tc_" + strings.ToLower(strings.ReplaceAll(tc.Name, " ", "_")) + tMetricDefinition := MetricDefinition{ + Name: tName, + Type: TypeHistogram, + Help: tc.Name, + CreateCollector: func() (prometheus.Collector, error) { + // prometheus gives default buckets if boundaries is empty. we want one big bucket instead + boundaries := tc.Input.Boundaries + if len(boundaries) == 0 { + boundaries = []float64{math.Inf(1)} + } + + return prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: tName, + Help: "My first test case", + Buckets: boundaries, + }, + slices.Collect(maps.Keys(tc.Input.Attributes)), + ), nil + }, + Update: func(collector prometheus.Collector, timestamp time.Time) error { + // Only update once + if time.Since(start) > 2*updatePeriod { + return nil + } + histogram, err := collector.(*prometheus.HistogramVec).GetMetricWith(tc.Input.Attributes) + if err != nil { + return err + } + for _, v := range generateDatapoints(tc.Input) { + histogram.Observe(v) + } + return nil + }, + } + + if err := generator.AddMetric(tMetricDefinition); err != nil { + log.Fatalf("unable to add metric: %v", err) + } + } + + // Start updating metrics periodically + go func() { + ticker := time.NewTicker(updatePeriod) + defer ticker.Stop() + for t := range ticker.C { + if err := generator.UpdateMetrics(t); err != nil { + log.Printf("Error updating metrics: %v", err) + } + } + }() + + // Start HTTP server + http.Handle("/metrics", generator) + log.Printf("Starting server on :8080") + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func generateDatapoints(in histograms.HistogramInput) []float64 { + if in.Count == 0 { + return []float64{} + } + + dps := []float64{} + totalGenerated := 0.0 + + for i, count := range in.Counts { + if count == 0 { + continue + } + + var bucketValue float64 + if len(in.Boundaries) == 0 { + bucketValue = in.Sum / float64(in.Count) + } else if i == 0 { + if in.Min != nil { + bucketValue = (*in.Min + in.Boundaries[0]) / 2 + } else { + bucketValue = in.Boundaries[0] - 1 + } + } else if i < len(in.Boundaries) { + bucketValue = (in.Boundaries[i-1] + in.Boundaries[i]) / 2 + } else { + if in.Max != nil { + bucketValue = (in.Boundaries[len(in.Boundaries)-1] + *in.Max) / 2 + } else { + bucketValue = in.Boundaries[len(in.Boundaries)-1] + 1 + } + } + + for j := uint64(0); j < count; j++ { + dps = append(dps, bucketValue) + totalGenerated += bucketValue + } + } + + if len(dps) > 0 && totalGenerated != 0 && len(in.Boundaries) > 0 { + ratio := in.Sum / totalGenerated + for i := range dps { + dps[i] *= ratio + } + } + return dps +} From f299a504301d19964736145f0ec2d757b903e9d1 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Fri, 12 Sep 2025 12:07:07 -0400 Subject: [PATCH 03/23] Add untyped metric support --- cmd/promgen/metrics.go | 13 ++++++++++++- cmd/promgen/promgen.go | 13 +++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/cmd/promgen/metrics.go b/cmd/promgen/metrics.go index ef577f8bd5945..492c45b762525 100644 --- a/cmd/promgen/metrics.go +++ b/cmd/promgen/metrics.go @@ -151,6 +151,18 @@ func (g *Generator) serveProtobuf(w http.ResponseWriter, r *http.Request) { func (g *Generator) defaultCollector(def MetricDefinition) (prometheus.Collector, error) { switch def.Type { + case TypeUntyped: + return prometheus.NewUntypedFunc( + prometheus.UntypedOpts{ + Name: "my_untyped_metric", + Help: "An example of an untyped metric.", + }, + func() float64 { + // This function will be called whenever Prometheus scrapes + // the metric, returning the current value. + return 42.0 // Example value + }, + ), nil case TypeCounter: return prometheus.NewCounterVec( prometheus.CounterOpts{ @@ -198,7 +210,6 @@ func (g *Generator) defaultCollector(def MetricDefinition) (prometheus.Collector }, def.Labels, ), nil - default: return nil, fmt.Errorf("unsupported metric type: %s", def.Type) } diff --git a/cmd/promgen/promgen.go b/cmd/promgen/promgen.go index bf5452d3cec6d..49e7455f5c84c 100644 --- a/cmd/promgen/promgen.go +++ b/cmd/promgen/promgen.go @@ -20,6 +20,19 @@ func main() { start := time.Now() generator := NewGenerator() + untypedMetric := MetricDefinition{ + Name: "untyped_metric", + Type: TypeUntyped, + Help: "An untyped metric that always returns 42", + Update: func(collector prometheus.Collector, timestamp time.Time) error { + return nil + }, + } + + if err := generator.AddMetric(untypedMetric); err != nil { + log.Fatalf("unable to add metric: %v", err) + } + monotonicCounter := MetricDefinition{ Name: "monotonic_counter", Type: TypeCounter, From 32239f154af91b5b1a1310e8c6f036c4164e96b4 Mon Sep 17 00:00:00 2001 From: okankoAMZ <107267850+okankoAMZ@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:32:35 -0700 Subject: [PATCH 04/23] Add OpenTelemetry Histogram Generator (#366) feat: implement statistical histogram generator with OpenTelemetry integration - Add comprehensive statistical distribution support: - Gamma, Weibull, LogNormal, Beta, and Exponential distributions - Sinusoidal, spiky, and trending value generators - Realistic metric patterns (CPU cycles, memory leaks, response times) - Integrate with OpenTelemetry: - Replace manual OTLP with official telemetrygen package - Add conversion utilities for percentile ranges - Implement dynamic histogram generation capabilities - Improve code organization: - Split generator into focused modules (types, distributions, publisher) - Follow single-responsibility design principles - Add comprehensive documentation and examples - Include tests and usage examples This implements a complete histogram generation system for realistic OpenTelemetry test data with configurable statistical distributions. --- cmd/generator/README.md | 147 +++++++ cmd/generator/distributions.go | 92 ++++ cmd/generator/example_test.go | 632 +++++++++++++++++++++++++++ cmd/generator/generator.go | 33 ++ cmd/generator/go.mod | 35 ++ cmd/generator/go.sum | 53 +++ cmd/generator/histogram_generator.go | 222 ++++++++++ cmd/generator/otlp_publisher.go | 74 ++++ cmd/generator/types.go | 43 ++ go.mod | 30 ++ go.sum | 83 ++++ 11 files changed, 1444 insertions(+) create mode 100644 cmd/generator/README.md create mode 100644 cmd/generator/distributions.go create mode 100644 cmd/generator/example_test.go create mode 100644 cmd/generator/generator.go create mode 100644 cmd/generator/go.mod create mode 100644 cmd/generator/go.sum create mode 100644 cmd/generator/histogram_generator.go create mode 100644 cmd/generator/otlp_publisher.go create mode 100644 cmd/generator/types.go create mode 100644 go.sum diff --git a/cmd/generator/README.md b/cmd/generator/README.md new file mode 100644 index 0000000000000..6cf0bab8684de --- /dev/null +++ b/cmd/generator/README.md @@ -0,0 +1,147 @@ +# Histogram Generator + +A Go package for generating realistic histogram data for OpenTelemetry metrics testing. This package provides statistical distribution functions and can optionally publish generated metrics to OTLP endpoints. + +## Architecture + +The package is organized into focused modules for maintainability and scalability: + +``` +cmd/generator/ +├── generator.go # Package documentation and overview +├── types.go # Core data structures and types +├── histogram_generator.go # Main histogram generation logic +├── distributions.go # Statistical distribution functions +├── otlp_publisher.go # OTLP endpoint publishing functionality +└── example_test.go # Usage examples +``` + +### Key Components + +- **HistogramGenerator**: Main generator that creates histogram data from statistical distributions +- **OTLPPublisher**: Handles publishing metrics to OpenTelemetry Protocol endpoints using both telemetrygen and custom OTLP +- **Distribution Functions**: Various statistical distributions (Normal, Exponential, Gamma, etc.) +- **Types**: Shared data structures for histogram inputs, outputs, and configuration + +### Publishing Approach + +The package uses the official OpenTelemetry telemetrygen package for all metric publishing, ensuring: + +- **Standard Compliance**: Full compatibility with OTLP endpoints +- **Reliability**: Uses the same code as the official telemetrygen tool +- **Simplicity**: Clean API with `metrics.Start(cfg)` under the hood +- **Flexibility**: Supports Gauge, Sum, and Histogram metric types + +The histogram generator creates realistic data distributions, while telemetrygen handles the actual OTLP publishing. + +## Usage + +### Basic Generation + +```go +generator := NewHistogramGenerator(GenerationOptions{ + Seed: 12345, // For reproducible results +}) + +input := HistogramInput{ + Count: 1000, + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Attributes: map[string]string{"service.name": "test-service"}, +} + +result, err := generator.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return NormalRandom(rnd, 75, 25) // mean=75, stddev=25 +}) +``` + +### Generation with Publishing + +```go +generator := NewHistogramGenerator(GenerationOptions{ + Seed: time.Now().UnixNano(), + Endpoint: "localhost:4318", // OTLP HTTP endpoint +}) + +result, err := generator.GenerateAndPublishHistograms(input, valueFunc) +``` + +### Direct Publishing with Telemetrygen + +You can also use telemetrygen directly for different metric types: + +```go +publisher := NewOTLPPublisher("localhost:4318") + +// Send different types of metrics using telemetrygen +err := publisher.SendSumMetric("requests_total", 100) +err = publisher.SendGaugeMetric("cpu_usage", 75.5) +err = publisher.SendHistogramMetricSimple("response_time") +``` + +This approach uses the official OpenTelemetry telemetrygen package under the hood, ensuring compatibility with standard OTLP endpoints. + +## Available Distributions + +- **NormalRandom**: Normal (Gaussian) distribution +- **ExponentialRandom**: Exponential distribution +- **GammaRandom**: Gamma distribution +- **LogNormalRandom**: Log-normal distribution +- **WeibullRandom**: Weibull distribution +- **BetaRandom**: Beta distribution + +### Time-based Functions + +- **SinusoidalValue**: Sinusoidal patterns with noise +- **SpikyValue**: Baseline with occasional spikes +- **TrendingValue**: Linear trend with noise + +## Design Principles + +### Single Responsibility +Each file has a focused purpose: +- `types.go`: Data structures only +- `distributions.go`: Statistical functions only +- `histogram_generator.go`: Core generation logic only +- `otlp_publisher.go`: Publishing logic only + +### Dependency Injection +The generator accepts value functions, allowing for flexible distribution selection and custom patterns. + +### Testability +All components are designed for easy unit testing with deterministic seeds and dependency injection. + +### Extensibility +New distributions can be added to `distributions.go` without affecting other components. + +## Features + +- ✅ **Statistical Distributions**: Multiple distribution functions for realistic data +- ✅ **OTLP Publishing**: Direct integration with OpenTelemetry Protocol endpoints +- ✅ **Flexible Generation**: Custom value functions and deterministic seeds +- ✅ **Multiple Metric Types**: Support for Gauge, Sum, and Histogram metrics + +## Future Enhancements + +1. **Additional Publishers**: Support for Prometheus, StatsD, etc. +2. **More Distributions**: Poisson, Binomial, etc. +3. **Validation**: Input validation for histogram consistency +4. **Batch Generation**: Generate multiple histograms efficiently +5. **Configuration Files**: YAML/JSON configuration support + +## Testing + +The package includes comprehensive examples in `example_test.go` and integrates with the test cases in `share/testdata/histograms/`. + +Run tests: +```bash +go test ./cmd/generator/... +``` + +## Integration + +This generator is used by: +- `share/testdata/histograms/histograms.go`: Test case generation +- Various metric exporters for testing realistic data patterns +- Performance testing tools for load generation \ No newline at end of file diff --git a/cmd/generator/distributions.go b/cmd/generator/distributions.go new file mode 100644 index 0000000000000..55144d10f3979 --- /dev/null +++ b/cmd/generator/distributions.go @@ -0,0 +1,92 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "math" + "math/rand" + "time" +) + +// Distribution functions for generating statistical data + +func ExponentialRandom(rnd *rand.Rand, rate float64) float64 { + return -math.Log(1.0-rnd.Float64()) / rate +} + +func NormalRandom(rnd *rand.Rand, mean, stddev float64) float64 { + return rnd.NormFloat64()*stddev + mean +} + +func LogNormalRandom(rnd *rand.Rand, mu, sigma float64) float64 { + return math.Exp(NormalRandom(rnd, mu, sigma)) +} + +func WeibullRandom(rnd *rand.Rand, shape, scale float64) float64 { + return scale * math.Pow(-math.Log(1.0-rnd.Float64()), 1.0/shape) +} + +func BetaRandom(rnd *rand.Rand, alpha, beta float64) float64 { + x := GammaRandom(rnd, alpha, 1.0) + y := GammaRandom(rnd, beta, 1.0) + return x / (x + y) +} + +func GammaRandom(rnd *rand.Rand, alpha, beta float64) float64 { + if alpha < 1.0 { + // Use Johnk's generator for alpha < 1 + for { + u := rnd.Float64() + v := rnd.Float64() + x := math.Pow(u, 1.0/alpha) + y := math.Pow(v, 1.0/(1.0-alpha)) + if x+y <= 1.0 { + if x+y > 0 { + return beta * x / (x + y) * (-math.Log(rnd.Float64())) + } + } + } + } + + // Marsaglia and Tsang's method for alpha >= 1 + d := alpha - 1.0/3.0 + c := 1.0 / math.Sqrt(9.0*d) + + for { + x := rnd.NormFloat64() + v := 1.0 + c*x + if v <= 0 { + continue + } + v = v * v * v + u := rnd.Float64() + if u < 1.0-0.0331*(x*x)*(x*x) { + return beta * d * v + } + if math.Log(u) < 0.5*x*x+d*(1.0-v+math.Log(v)) { + return beta * d * v + } + } +} + +// Time-based value functions + +func SinusoidalValue(rnd *rand.Rand, timestamp time.Time, amplitude, period, phase, baseline float64) float64 { + t := float64(timestamp.Unix()) + noise := rnd.NormFloat64() * amplitude * 0.1 // 10% noise + return baseline + amplitude*math.Sin(2*math.Pi*t/period+phase) + noise +} + +func SpikyValue(rnd *rand.Rand, baseline, spikeHeight, spikeProb float64) float64 { + if rnd.Float64() < spikeProb { + return baseline + spikeHeight*rnd.Float64() + } + return baseline + rnd.NormFloat64()*baseline*0.1 +} + +func TrendingValue(rnd *rand.Rand, timestamp time.Time, startValue, trendRate, noise float64) float64 { + t := float64(timestamp.Unix()) + trend := startValue + trendRate*t + return trend + rnd.NormFloat64()*noise +} diff --git a/cmd/generator/example_test.go b/cmd/generator/example_test.go new file mode 100644 index 0000000000000..47064a3656f19 --- /dev/null +++ b/cmd/generator/example_test.go @@ -0,0 +1,632 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package generator_test + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/amazon-contributing/opentelemetry-collector-contrib/cmd/generator" +) + +// TestHistogramGenerator_GenerateHistogram_Example demonstrates basic histogram generation +// CURRENT CAPABILITY: Statistical histogram data generation with custom distributions +func TestHistogramGenerator_GenerateHistogram_Example(t *testing.T) { + fmt.Println("=== CURRENT GOAL: Statistical Histogram Data Generation ===") + + // Create a generator with a fixed seed for reproducible results + gen := generator.NewHistogramGenerator(generator.GenerationOptions{ + Seed: 12345, + }) + + // Define histogram input parameters + input := generator.HistogramInput{ + Count: 1000, + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Attributes: map[string]string{ + "service.name": "payment-service", + "environment": "production", + }, + } + + // Generate histogram using normal distribution + result, err := gen.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 25) // mean=75, stddev=25 + }) + + if err != nil { + panic(err) + } + + fmt.Printf("✅ Generated %d samples with sum=%.2f, avg=%.2f\n", + result.Expected.Count, result.Expected.Sum, result.Expected.Average) + fmt.Printf("✅ Min=%.2f, Max=%.2f\n", *result.Expected.Min, *result.Expected.Max) + fmt.Printf("✅ Bucket distribution: %v\n", result.Input.Counts) + + // This example demonstrates histogram generation with statistical distributions. + // Output will vary due to randomness, but structure is consistent. +} + +// TestHieHistogrenerator_GenerateAndPublishHistograms_Example shows telemetrygen integration +// CURRENT LIMITATION: Only supports basic histogram publishing, not custom bucket data +func TestHistogramGenerator_GenerateAndPublishHistograms_Example(t *testing.T) { + // Make sure collector is running before removing the skip on this function + t.Skip() + fmt.Println("=== CURRENT GOAL: Basic OTLP Publishing via Telemetrygen ===") + + // Create a generator with OTLP endpoint + gen := generator.NewHistogramGenerator(generator.GenerationOptions{ + Seed: time.Now().UnixNano(), + Endpoint: "localhost:4318", // OTLP HTTP endpoint + }) + + input := generator.HistogramInput{ + Count: 500, + Boundaries: []float64{10, 50, 100, 500, 1000}, + Attributes: map[string]string{ + "service.name": "web-service", + "service.version": "1.0.0", + "environment": "staging", + }, + } + + // Generate and publish using exponential distribution + result, err := gen.GenerateAndPublishHistograms(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.ExponentialRandom(rnd, 0.01) // rate=0.01 + }) + + if err != nil { + fmt.Printf("❌ Error (expected - telemetrygen limitations): %v\n", err) + return + } + + fmt.Printf("✅ Generated and published histogram with %d samples\n", result.Expected.Count) + fmt.Printf("⚠️ Note: Uses telemetrygen's built-in histogram generation, not custom buckets\n") +} + +// TestOTLPPublisher_SendSumMetric_Example demonstrates current telemetrygen integration +// CURRENT CAPABILITY: Basic Sum, Gauge, Histogram via telemetrygen +// MISSING: Delta/Cumulative distinction, Summary, Exponential Histogram +func TestOTLPPublisher_SendSumMetric_Example(t *testing.T) { + // Make sure collector is running before removing the skip on this function + t.Skip() + fmt.Println("=== CURRENT GOAL: Basic Metric Types via Telemetrygen ===") + + publisher := generator.NewOTLPPublisher("localhost:4318") + + // ✅ WORKING: Basic metric types supported by telemetrygen + err := publisher.SendSumMetric("requests_total", 100) + if err != nil { + fmt.Printf("❌ Error sending sum metric: %v\n", err) + return + } + fmt.Println("✅ Sum metric sent (telemetrygen)") + + err = publisher.SendGaugeMetric("cpu_usage", 75.5) + if err != nil { + fmt.Printf("❌ Error sending gauge metric: %v\n", err) + return + } + fmt.Println("✅ Gauge metric sent (telemetrygen)") + + err = publisher.SendHistogramMetricSimple("response_time") + if err != nil { + fmt.Printf("❌ Error sending histogram metric: %v\n", err) + return + } + fmt.Println("✅ Basic histogram sent (telemetrygen)") + + fmt.Println("\n⚠️ LIMITATIONS:") + fmt.Println(" - No delta vs cumulative temporality control") + fmt.Println(" - No summary metrics") + fmt.Println(" - No exponential histograms") + fmt.Println(" - No custom histogram bucket data") +} + +// TestAllDistributions_Example demonstrates all available statistical distributions +// CURRENT CAPABILITY: Complete statistical distribution library +func TestAllDistributions_Example(t *testing.T) { + fmt.Println("=== CURRENT CAPABILITY: All Statistical Distributions ===") + + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 42}) + boundaries := []float64{10, 25, 50, 75, 100, 150, 200} + attributes := map[string]string{"test": "distributions"} + + fmt.Println("\n📊 PROBABILITY DISTRIBUTIONS:") + + // Normal Distribution + result, _ := gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 20) // mean=75, stddev=20 + }) + fmt.Printf("✅ Normal (μ=75, σ=20): avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) + + // Exponential Distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.ExponentialRandom(rnd, 0.02) // rate=0.02 + }) + fmt.Printf("✅ Exponential (λ=0.02): avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) + + // Log-Normal Distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.LogNormalRandom(rnd, 3.5, 0.8) // mu=3.5, sigma=0.8 + }) + fmt.Printf("✅ Log-Normal (μ=3.5, σ=0.8): avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) + + // Gamma Distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.GammaRandom(rnd, 2.0, 25.0) // shape=2, scale=25 + }) + fmt.Printf("✅ Gamma (α=2, β=25): avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) + + // Weibull Distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.WeibullRandom(rnd, 2.5, 80) // shape=2.5, scale=80 + }) + fmt.Printf("✅ Weibull (k=2.5, λ=80): avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) + + // Beta Distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.BetaRandom(rnd, 2, 5) * 200 // scale to 0-200 range + }) + fmt.Printf("✅ Beta (α=2, β=5) scaled: avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) +} + +// TestTimeBasedPatterns_Example demonstrates time-based value generation +// CURRENT CAPABILITY: Dynamic time-based patterns for realistic metrics +func TestTimeBasedPatterns_Example(t *testing.T) { + fmt.Println("\n=== CURRENT CAPABILITY: Time-Based Patterns ===") + + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 123}) + boundaries := []float64{20, 40, 60, 80, 100, 120} + attributes := map[string]string{"pattern": "time-based"} + + fmt.Println("\n🕐 TIME-BASED FUNCTIONS:") + + // Sinusoidal Pattern (daily cycle) + result, _ := gen.GenerateHistogram(generator.HistogramInput{ + Count: 500, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.SinusoidalValue(rnd, t, 30, 86400, 0, 60) // 24-hour cycle + }) + fmt.Printf("✅ Sinusoidal (24h cycle): avg=%.1f, range=[%.1f-%.1f]\n", + result.Expected.Average, *result.Expected.Min, *result.Expected.Max) + + // Spiky Pattern (occasional bursts) + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 500, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.SpikyValue(rnd, 50, 100, 0.1) // 10% spike probability + }) + fmt.Printf("✅ Spiky (10%% spikes): avg=%.1f, range=[%.1f-%.1f]\n", + result.Expected.Average, *result.Expected.Min, *result.Expected.Max) + + // Trending Pattern (gradual increase) + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 500, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.TrendingValue(rnd, t, 40, 0.0001, 8) // slow upward trend + }) + fmt.Printf("✅ Trending (upward): avg=%.1f, range=[%.1f-%.1f]\n", + result.Expected.Average, *result.Expected.Min, *result.Expected.Max) +} + +// TestAdvancedHistogramFeatures_Example shows advanced histogram generation capabilities +// CURRENT CAPABILITY: Custom buckets, attributes, statistical validation +func TestAdvancedHistogramFeatures_Example(t *testing.T) { + fmt.Println("\n=== CURRENT CAPABILITY: Advanced Histogram Features ===") + + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 999}) + + fmt.Println("\n🔧 ADVANCED FEATURES:") + + // Custom bucket boundaries for different use cases + latencyBoundaries := []float64{1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000} + sizeBoundaries := []float64{1024, 4096, 16384, 65536, 262144, 1048576, 4194304} + + // Latency histogram with log-normal distribution + result, _ := gen.GenerateHistogram(generator.HistogramInput{ + Count: 2000, + Min: ptr(0.5), + Max: ptr(10000.0), + Boundaries: latencyBoundaries, + Attributes: map[string]string{ + "service.name": "api-gateway", + "endpoint": "/users", + "method": "GET", + "status_code": "200", + }, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.LogNormalRandom(rnd, 3.0, 1.2) // realistic latency distribution + }) + fmt.Printf("✅ Latency histogram: %d samples, avg=%.1fms\n", + result.Expected.Count, result.Expected.Average) + fmt.Printf(" Buckets: %v\n", result.Input.Counts) + + // File size histogram with exponential distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1500, + Boundaries: sizeBoundaries, + Attributes: map[string]string{ + "file_type": "image", + "compression": "jpeg", + "quality": "high", + }, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.ExponentialRandom(rnd, 0.000001) // file size distribution + }) + fmt.Printf("✅ File size histogram: %d samples, avg=%.0f bytes\n", + result.Expected.Count, result.Expected.Average) + + // Multi-modal distribution (combining two normals) + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, + Boundaries: []float64{10, 30, 50, 70, 90, 110, 130, 150}, + Attributes: map[string]string{ + "distribution": "bimodal", + "use_case": "response_time", + }, + }, func(rnd *rand.Rand, t time.Time) float64 { + if rnd.Float64() < 0.7 { + return generator.NormalRandom(rnd, 40, 10) // fast responses (70%) + } + return generator.NormalRandom(rnd, 120, 15) // slow responses (30%) + }) + fmt.Printf("✅ Bimodal distribution: avg=%.1f (fast+slow responses)\n", + result.Expected.Average) +} + +// TestCurrentPublishingCapabilities_Example shows what publishing works today +// CURRENT CAPABILITY: Basic OTLP publishing via telemetrygen +func TestCurrentPublishingCapabilities_Example(t *testing.T) { + // Make sure collector is running before removing the skip on this function + t.Skip() + fmt.Println("\n=== CURRENT CAPABILITY: OTLP Publishing ===") + + publisher := generator.NewOTLPPublisher("localhost:4318") + + fmt.Println("\n📡 WORKING METRIC TYPES:") + + // These work with current telemetrygen integration + metrics := []struct { + name string + metricType string + value float64 + status string + }{ + {"http_requests_total", "Sum", 1500, "✅ Counter/Sum"}, + {"cpu_utilization", "Gauge", 67.8, "✅ Gauge"}, + {"response_time_histogram", "Histogram", 0, "✅ Basic Histogram"}, + {"memory_usage_bytes", "Gauge", 2147483648, "✅ Gauge (large values)"}, + {"error_rate", "Gauge", 0.025, "✅ Gauge (fractional)"}, + } + + for _, m := range metrics { + err := publisher.SendMetric(m.name, m.metricType, m.value) + if err != nil { + fmt.Printf("❌ %s: %v\n", m.status, err) + } else { + fmt.Printf("%s: %s (%.2f)\n", m.status, m.name, m.value) + } + } + + fmt.Println("\n⚠️ TELEMETRYGEN LIMITATIONS:") + fmt.Println(" - No custom histogram bucket data") + fmt.Println(" - No temporality control (delta vs cumulative)") + fmt.Println(" - No summary metrics") + fmt.Println(" - No exponential histograms") +} + +// TestShowCapabilities demonstrates all current capabilities +func TestShowCapabilities(t *testing.T) { + fmt.Println("\n🎯 RUNNING CAPABILITY DEMONSTRATION") + + // Run all the example functions to show capabilities + TestHistogramGenerator_GenerateHistogram_Example(t) + fmt.Println() + + // Skip publishing test that requires OTLP endpoint + fmt.Println("=== SKIPPING: OTLP Publishing (requires running collector) ===") + fmt.Println() + + TestAllDistributions_Example(t) + fmt.Println() + + TestTimeBasedPatterns_Example(t) + fmt.Println() + + TestAdvancedHistogramFeatures_Example(t) + fmt.Println() + + TestCurrentPublishingCapabilities_Example(t) + fmt.Println() + +} + +// TestHistogramGenerator_GenerateHistogram tests the core histogram generation functionality +func TestHistogramGenerator_GenerateHistogram(t *testing.T) { + gen := generator.NewHistogramGenerator(generator.GenerationOptions{ + Seed: 12345, // Fixed seed for reproducible tests + }) + + input := generator.HistogramInput{ + Count: 1000, + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Attributes: map[string]string{ + "service.name": "test-service", + }, + } + + result, err := gen.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 25) // mean=75, stddev=25 + }) + + // Test basic functionality + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + // Test sample count + if result.Expected.Count != 1000 { + t.Errorf("Expected 1000 samples, got %d", result.Expected.Count) + } + + // Test min/max constraints + if result.Expected.Min == nil || *result.Expected.Min < 10.0 { + t.Errorf("Expected min >= 10.0, got %v", result.Expected.Min) + } + if result.Expected.Max == nil || *result.Expected.Max > 200.0 { + t.Errorf("Expected max <= 200.0, got %v", result.Expected.Max) + } + + // Test that average is reasonable for normal distribution (mean=75) + if result.Expected.Average < 60 || result.Expected.Average > 90 { + t.Errorf("Expected average around 75 (60-90), got %.2f", result.Expected.Average) + } + + // Test bucket counts + if len(result.Input.Counts) != len(input.Boundaries)+1 { + t.Errorf("Expected %d buckets, got %d", len(input.Boundaries)+1, len(result.Input.Counts)) + } + + // Test that sum equals count * average (approximately) + expectedSum := float64(result.Expected.Count) * result.Expected.Average + if abs(result.Expected.Sum-expectedSum) > 1.0 { + t.Errorf("Sum/average mismatch: sum=%.2f, count*avg=%.2f", result.Expected.Sum, expectedSum) + } + + // Test attributes are preserved + if result.Input.Attributes["service.name"] != "test-service" { + t.Errorf("Expected service.name=test-service, got %s", result.Input.Attributes["service.name"]) + } +} + +// TestHistogramGenerator_Distributions tests all statistical distributions +func TestHistogramGenerator_Distributions(t *testing.T) { + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 42}) + boundaries := []float64{10, 25, 50, 75, 100} + input := generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, + } + + distributions := []struct { + name string + valueFunc func(*rand.Rand, time.Time) float64 + minAvg float64 + maxAvg float64 + }{ + { + name: "Normal", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 50, 10) + }, + minAvg: 40, maxAvg: 60, + }, + { + name: "Exponential", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.ExponentialRandom(rnd, 0.02) + }, + minAvg: 30, maxAvg: 70, + }, + { + name: "Gamma", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.GammaRandom(rnd, 2.0, 25.0) + }, + minAvg: 40, maxAvg: 60, + }, + } + + for _, dist := range distributions { + t.Run(dist.name, func(t *testing.T) { + result, err := gen.GenerateHistogram(input, dist.valueFunc) + + if err != nil { + t.Fatalf("Distribution %s failed: %v", dist.name, err) + } + + if result.Expected.Count != 1000 { + t.Errorf("Distribution %s: expected 1000 samples, got %d", dist.name, result.Expected.Count) + } + + if result.Expected.Average < dist.minAvg || result.Expected.Average > dist.maxAvg { + t.Errorf("Distribution %s: average %.2f outside expected range [%.1f-%.1f]", + dist.name, result.Expected.Average, dist.minAvg, dist.maxAvg) + } + }) + } +} + +// TestOTLPPublisher_Creation tests OTLP publisher creation +func TestOTLPPublisher_Creation(t *testing.T) { + t.Skip() + publisher := generator.NewOTLPPublisher("localhost:4318") + + if publisher == nil { + t.Fatal("Expected publisher to be created, got nil") + } + + // Test that publisher has the expected endpoint (this would require exposing the field or adding a getter) + // For now, just test that it was created successfully +} + +// TestGenerationOptions tests the generation options +func TestGenerationOptions(t *testing.T) { + // Test with seed + gen1 := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 123}) + gen2 := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 123}) + + input := generator.HistogramInput{Count: 100, Boundaries: []float64{50, 100}} + + result1, err1 := gen1.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 10) + }) + + result2, err2 := gen2.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 10) + }) + + if err1 != nil || err2 != nil { + t.Fatalf("Expected no errors, got %v, %v", err1, err2) + } + + // With same seed, results should be identical + if result1.Expected.Sum != result2.Expected.Sum { + t.Errorf("Expected identical sums with same seed, got %.2f vs %.2f", + result1.Expected.Sum, result2.Expected.Sum) + } +} + +// TestTimeBasedPatterns tests time-based value functions +func TestTimeBasedPatterns(t *testing.T) { + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 999}) + input := generator.HistogramInput{Count: 100, Boundaries: []float64{25, 50, 75, 100}} + + patterns := []struct { + name string + valueFunc func(*rand.Rand, time.Time) float64 + }{ + { + name: "Sinusoidal", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.SinusoidalValue(rnd, t, 20, 3600, 0, 50) + }, + }, + { + name: "Spiky", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.SpikyValue(rnd, 50, 100, 0.1) + }, + }, + { + name: "Trending", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.TrendingValue(rnd, t, 40, 0.001, 5) + }, + }, + } + + for _, pattern := range patterns { + t.Run(pattern.name, func(t *testing.T) { + result, err := gen.GenerateHistogram(input, pattern.valueFunc) + + if err != nil { + t.Fatalf("Pattern %s failed: %v", pattern.name, err) + } + + if result.Expected.Count != 100 { + t.Errorf("Pattern %s: expected 100 samples, got %d", pattern.name, result.Expected.Count) + } + + // Test that we got reasonable values (not all zeros or infinities) + if result.Expected.Sum <= 0 || result.Expected.Average <= 0 { + t.Errorf("Pattern %s: got unreasonable values sum=%.2f, avg=%.2f", + pattern.name, result.Expected.Sum, result.Expected.Average) + } + }) + } +} + +// TestEdgeCases tests edge cases and error conditions +func TestEdgeCases(t *testing.T) { + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 1}) + + t.Run("ZeroCount", func(t *testing.T) { + input := generator.HistogramInput{Count: 0} + result, err := gen.GenerateHistogram(input, nil) + + if err != nil { + t.Fatalf("Expected no error with zero count, got %v", err) + } + + // Should default to some reasonable count + if result.Expected.Count == 0 { + t.Error("Expected non-zero count when input count is 0") + } + }) + + t.Run("NilValueFunc", func(t *testing.T) { + input := generator.HistogramInput{Count: 10} + result, err := gen.GenerateHistogram(input, nil) + + if err != nil { + t.Fatalf("Expected no error with nil value func, got %v", err) + } + + if result.Expected.Count == 0 { + t.Error("Expected samples even with nil value function") + } + }) + + t.Run("EmptyBoundaries", func(t *testing.T) { + input := generator.HistogramInput{Count: 10, Boundaries: []float64{}} + result, err := gen.GenerateHistogram(input, nil) + + if err != nil { + t.Fatalf("Expected no error with empty boundaries, got %v", err) + } + + // Should generate default boundaries + if len(result.Input.Boundaries) == 0 { + t.Error("Expected default boundaries when none provided") + } + }) +} + +// Helper function for absolute value +func abs(x float64) float64 { + if x < 0 { + return -x + } + return x +} + +// Helper function to create float64 pointers +func ptr(f float64) *float64 { + return &f +} diff --git a/cmd/generator/generator.go b/cmd/generator/generator.go new file mode 100644 index 0000000000000..86c6f5bd42480 --- /dev/null +++ b/cmd/generator/generator.go @@ -0,0 +1,33 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package generator provides histogram generation capabilities for OpenTelemetry metrics testing. +// This package is designed to generate realistic histogram data using various statistical distributions +// and can optionally publish the generated metrics to OTLP endpoints. +// +// The package is organized into several focused modules: +// - types.go: Core data structures and types +// - histogram_generator.go: Main histogram generation logic +// - distributions.go: Statistical distribution functions +// - otlp_publisher.go: OTLP endpoint publishing functionality +// +// Example usage: +// +// generator := NewHistogramGenerator(GenerationOptions{ +// Seed: 12345, +// Endpoint: "localhost:4318", +// }) +// +// result, err := generator.GenerateAndPublishHistograms( +// HistogramInput{ +// Count: 1000, +// Min: ptr(10.0), +// Max: ptr(200.0), +// Boundaries: []float64{25, 50, 75, 100, 150}, +// Attributes: map[string]string{"service.name": "test-service"}, +// }, +// func(rnd *rand.Rand, t time.Time) float64 { +// return NormalRandom(rnd, 50, 15) +// }, +// ) +package generator diff --git a/cmd/generator/go.mod b/cmd/generator/go.mod new file mode 100644 index 0000000000000..23649835c206a --- /dev/null +++ b/cmd/generator/go.mod @@ -0,0 +1,35 @@ +module github.com/amazon-contributing/opentelemetry-collector-contrib/cmd/generator + +go 1.24.7 + +require ( + github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 + go.opentelemetry.io/otel/metric v1.37.0 + go.opentelemetry.io/otel/sdk/metric v1.37.0 +) + +require ( + github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/sdk v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/time v0.12.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 // indirect + google.golang.org/grpc v1.75.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect +) diff --git a/cmd/generator/go.sum b/cmd/generator/go.sum new file mode 100644 index 0000000000000..08edfdd2b9f6e --- /dev/null +++ b/cmd/generator/go.sum @@ -0,0 +1,53 @@ +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0 h1:/PSf7CIVu//VV7zYeYhnOLIgMsrONH37XV2mzeVtjZk= +github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0/go.mod h1:RpbRtcf6cXpgn8aJOf6SvIuwGo2ycRUEI2HASamCjlw= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 h1:zG8GlgXCJQd5BU98C0hZnBbElszTmUgCNCfYneaDL0A= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0/go.mod h1:hOfBCz8kv/wuq73Mx2H2QnWokh/kHZxkh6SNF2bdKtw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 h1:9PgnL3QNlj10uGxExowIDIZu66aVBwWhXmbOp1pa6RA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0/go.mod h1:0ineDcLELf6JmKfuo0wvvhAVMuxWFYvkTin2iV4ydPQ= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0 h1:0UOBWO4dC+e51ui0NFKSPbkHHiQ4TmrEfEZMLDyRmY8= +google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0/go.mod h1:8ytArBbtOy2xfht+y2fqKd5DRDJRUQhqbyEnQ4bDChs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 h1:MAKi5q709QWfnkkpNQ0M12hYJ1+e8qYVDyowc4U1XZM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= diff --git a/cmd/generator/histogram_generator.go b/cmd/generator/histogram_generator.go new file mode 100644 index 0000000000000..0912a7abbf95a --- /dev/null +++ b/cmd/generator/histogram_generator.go @@ -0,0 +1,222 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "math" + "math/rand" + "sort" + "time" +) + +// HistogramGenerator generates histogram test cases using statistical distributions +type HistogramGenerator struct { + rand *rand.Rand + endpoint string +} + +// NewHistogramGenerator creates a new histogram generator with deterministic seed +func NewHistogramGenerator(opt ...GenerationOptions) *HistogramGenerator { + var seed int64 = time.Now().UnixNano() + var endpoint string + + if len(opt) > 0 { + if opt[0].Seed != 0 { + seed = opt[0].Seed + } + endpoint = opt[0].Endpoint + } + + return &HistogramGenerator{ + rand: rand.New(rand.NewSource(seed)), + endpoint: endpoint, + } +} + +// GenerateHistogram generates histogram data from individual values using a value function +func (g *HistogramGenerator) GenerateHistogram(input HistogramInput, valueFunc func(*rand.Rand, time.Time) float64) (HistogramResult, error) { + timestamp := time.Now() + sampleCount := int(input.Count) + + if sampleCount <= 0 { + sampleCount = 1000 // default sample count + } + + // Generate individual values using the value function + values := make([]float64, sampleCount) + for i := 0; i < sampleCount; i++ { + if valueFunc != nil { + values[i] = valueFunc(g.rand, timestamp) + } else { + values[i] = g.rand.Float64() * 100 // default random value + } + } + + // Sort values to find min/max + sort.Float64s(values) + + // Calculate basic stats + var sum float64 + for _, v := range values { + sum += v + } + + generatedMin := values[0] + generatedMax := values[len(values)-1] + average := sum / float64(len(values)) + + // Determine final min/max values + var finalMin, finalMax float64 + if input.Min != nil { + finalMin = *input.Min + } else { + finalMin = generatedMin + } + if input.Max != nil { + finalMax = *input.Max + } else { + finalMax = generatedMax + } + + // Use provided boundaries or generate them based on min/max + boundaries := input.Boundaries + if len(boundaries) == 0 { + boundaries = generateBoundariesBetween(finalMin, finalMax, 10) + } + + counts := make([]uint64, len(boundaries)+1) + for _, value := range values { + bucketIndex := len(boundaries) // default to overflow bucket + for i, boundary := range boundaries { + if value <= boundary { + bucketIndex = i + break + } + } + counts[bucketIndex]++ + } + + // Calculate percentile ranges + percentileRanges := g.calculatePercentileRangesFromValues(values, boundaries) + + // Use input min/max if provided, otherwise use generated values + var resultMin, resultMax *float64 + if input.Min != nil { + resultMin = input.Min + } else { + resultMin = &generatedMin + } + if input.Max != nil { + resultMax = input.Max + } else { + resultMax = &generatedMax + } + + generatedInput := HistogramInput{ + Count: uint64(len(values)), + Sum: sum, + Min: resultMin, + Max: resultMax, + Boundaries: boundaries, + Counts: counts, + Attributes: input.Attributes, + } + + expected := ExpectedMetrics{ + Count: uint64(len(values)), + Sum: sum, + Average: average, + Min: resultMin, + Max: resultMax, + PercentileRanges: percentileRanges, + } + + return HistogramResult{ + Input: generatedInput, + Expected: expected, + }, nil +} + +// GenerateAndPublishHistograms generates and optionally publishes histogram data +func (g *HistogramGenerator) GenerateAndPublishHistograms(input HistogramInput, valueFunc func(*rand.Rand, time.Time) float64) (HistogramResult, error) { + res, err := g.GenerateHistogram(input, valueFunc) + if err != nil { + return HistogramResult{}, err + } + + if g.endpoint == "" { + return res, nil + } + + publisher := NewOTLPPublisher(g.endpoint) + err = publisher.SendHistogramMetric("TelemetryGen", res) + if err != nil { + return HistogramResult{}, err + } + + return res, nil +} + +// calculatePercentileRangesFromValues calculates percentile ranges for sorted values +func (g *HistogramGenerator) calculatePercentileRangesFromValues(sortedValues []float64, boundaries []float64) map[float64]PercentileRange { + percentiles := []float64{0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99} + ranges := make(map[float64]PercentileRange) + + for _, p := range percentiles { + index := int(p * float64(len(sortedValues))) + if index >= len(sortedValues) { + index = len(sortedValues) - 1 + } + + value := sortedValues[index] + + // Find which bucket this percentile value falls into + var low, high float64 + + // Check if value falls in any boundary bucket + bucketFound := false + for i, boundary := range boundaries { + if value <= boundary { + if i > 0 { + low = boundaries[i-1] + } else { + low = math.Inf(-1) + } + high = boundary + bucketFound = true + break + } + } + + // If not found in any boundary bucket, it's in the overflow bucket + if !bucketFound { + if len(boundaries) > 0 { + low = boundaries[len(boundaries)-1] + } else { + low = math.Inf(-1) + } + high = math.Inf(1) + } + + ranges[p] = PercentileRange{Low: low, High: high} + } + + return ranges +} + +// generateBoundariesBetween creates evenly spaced boundaries between min and max +func generateBoundariesBetween(min, max float64, numBuckets int) []float64 { + if numBuckets <= 0 { + numBuckets = 10 + } + + boundaries := make([]float64, numBuckets-1) + step := (max - min) / float64(numBuckets) + + for i := 0; i < numBuckets-1; i++ { + boundaries[i] = min + float64(i+1)*step + } + + return boundaries +} diff --git a/cmd/generator/otlp_publisher.go b/cmd/generator/otlp_publisher.go new file mode 100644 index 0000000000000..231fded29ef7a --- /dev/null +++ b/cmd/generator/otlp_publisher.go @@ -0,0 +1,74 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "fmt" + + "github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/pkg/metrics" +) + +// OTLPPublisher handles publishing metrics to OTLP endpoints using telemetrygen +type OTLPPublisher struct { + endpoint string +} + +// NewOTLPPublisher creates a new OTLP publisher using telemetrygen +func NewOTLPPublisher(endpoint string) *OTLPPublisher { + return &OTLPPublisher{ + endpoint: endpoint, + } +} + +// SendHistogramMetric sends a histogram metric using telemetrygen +func (p *OTLPPublisher) SendHistogramMetric(metricName string, result HistogramResult) error { + return p.SendMetric(metricName, "Histogram", 0) // Histogram value doesn't matter for telemetrygen +} + +// SendMetric sends a metric using telemetrygen with the specified type and value +func (p *OTLPPublisher) SendMetric(metricName string, metricType string, value float64) error { + // Create telemetrygen config + cfg := metrics.NewConfig() + cfg.CustomEndpoint = p.endpoint + cfg.UseHTTP = true + cfg.Insecure = true + cfg.NumMetrics = 1 + cfg.Rate = 1 + cfg.MetricName = metricName + + // Set metric type + switch metricType { + case "Sum": + cfg.MetricType = metrics.MetricTypeSum + case "Gauge": + cfg.MetricType = metrics.MetricTypeGauge + case "Histogram": + cfg.MetricType = metrics.MetricTypeHistogram + default: + cfg.MetricType = metrics.MetricTypeGauge // default to gauge + } + + // Start the metrics generation + err := metrics.Start(cfg) + if err != nil { + return fmt.Errorf("failed to send metric via telemetrygen: %v", err) + } + + return nil +} + +// SendGaugeMetric sends a gauge metric +func (p *OTLPPublisher) SendGaugeMetric(metricName string, value float64) error { + return p.SendMetric(metricName, "Gauge", value) +} + +// SendSumMetric sends a sum/counter metric +func (p *OTLPPublisher) SendSumMetric(metricName string, value float64) error { + return p.SendMetric(metricName, "Sum", value) +} + +// SendHistogramMetricSimple sends a histogram metric (telemetrygen will generate histogram data) +func (p *OTLPPublisher) SendHistogramMetricSimple(metricName string) error { + return p.SendMetric(metricName, "Histogram", 0) +} diff --git a/cmd/generator/types.go b/cmd/generator/types.go new file mode 100644 index 0000000000000..6660fd684d52e --- /dev/null +++ b/cmd/generator/types.go @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package generator + +// PercentileRange represents a range for percentile calculations +type PercentileRange struct { + Low float64 + High float64 +} + +// HistogramInput represents the input data for a histogram metric +type HistogramInput struct { + Count uint64 + Sum float64 + Min *float64 + Max *float64 + Boundaries []float64 + Counts []uint64 + Attributes map[string]string +} + +// ExpectedMetrics represents the expected calculated metrics from histogram data +type ExpectedMetrics struct { + Count uint64 + Sum float64 + Average float64 + Min *float64 + Max *float64 + PercentileRanges map[float64]PercentileRange +} + +// HistogramResult combines input and expected metrics +type HistogramResult struct { + Input HistogramInput + Expected ExpectedMetrics +} + +// GenerationOptions configures histogram generation +type GenerationOptions struct { + Seed int64 + Endpoint string +} diff --git a/go.mod b/go.mod index daff7f0ef1b51..35fae10821064 100644 --- a/go.mod +++ b/go.mod @@ -16,3 +16,33 @@ retract ( v0.65.0 v0.37.0 // Contains dependencies on v0.36.0 components, which should have been updated to v0.37.0. ) + +require github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0 + +require ( + github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/otel/sdk v1.37.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/time v0.12.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 // indirect + google.golang.org/grpc v1.75.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000..e158940b12a56 --- /dev/null +++ b/go.sum @@ -0,0 +1,83 @@ +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0 h1:/PSf7CIVu//VV7zYeYhnOLIgMsrONH37XV2mzeVtjZk= +github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0/go.mod h1:RpbRtcf6cXpgn8aJOf6SvIuwGo2ycRUEI2HASamCjlw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/collector/featuregate v1.41.0 h1:CL4UMsMQj35nMJC3/jUu8VvYB4MHirbAX4B0Z/fCVLY= +go.opentelemetry.io/collector/featuregate v1.41.0/go.mod h1:A72x92glpH3zxekaUybml1vMSv94BH6jQRn5+/htcjw= +go.opentelemetry.io/collector/pdata v1.41.0 h1:2zurAaY0FkURbLa1x7f7ag6HaNZYZKSmI4wgzDegLgo= +go.opentelemetry.io/collector/pdata v1.41.0/go.mod h1:h0OghaTYe4oRvLxK31Ny7gkyjJ1p8oniM5MiCzluQjc= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 h1:zG8GlgXCJQd5BU98C0hZnBbElszTmUgCNCfYneaDL0A= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0/go.mod h1:hOfBCz8kv/wuq73Mx2H2QnWokh/kHZxkh6SNF2bdKtw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 h1:9PgnL3QNlj10uGxExowIDIZu66aVBwWhXmbOp1pa6RA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0/go.mod h1:0ineDcLELf6JmKfuo0wvvhAVMuxWFYvkTin2iV4ydPQ= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0 h1:0UOBWO4dC+e51ui0NFKSPbkHHiQ4TmrEfEZMLDyRmY8= +google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0/go.mod h1:8ytArBbtOy2xfht+y2fqKd5DRDJRUQhqbyEnQ4bDChs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 h1:MAKi5q709QWfnkkpNQ0M12hYJ1+e8qYVDyowc4U1XZM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 33e11edba9af2ea4ebd320266be82047edbd673d Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Tue, 16 Sep 2025 19:32:39 -0400 Subject: [PATCH 05/23] local commit --- .../__pycache__/metrics_pb2.cpython-37.pyc | Bin 0 -> 3758 bytes cmd/promgen/metrics.go | 12 +- cmd/promgen/metrics.proto | 157 +++++++++++ cmd/promgen/metrics_pb2.py | 62 ++++ cmd/promgen/proto-reader.py | 32 +++ cmd/promgen/protobuf.bin | Bin 0 -> 879 bytes share/testdata/histograms/generator.go | 266 ++++++++++++++++++ share/testdata/histograms/generator_test.go | 18 ++ share/testdata/histograms/histograms.go | 15 +- share/testdata/histograms/histograms_test.go | 2 +- 10 files changed, 554 insertions(+), 10 deletions(-) create mode 100644 cmd/promgen/__pycache__/metrics_pb2.cpython-37.pyc create mode 100644 cmd/promgen/metrics.proto create mode 100644 cmd/promgen/metrics_pb2.py create mode 100644 cmd/promgen/proto-reader.py create mode 100644 cmd/promgen/protobuf.bin create mode 100644 share/testdata/histograms/generator.go create mode 100644 share/testdata/histograms/generator_test.go diff --git a/cmd/promgen/__pycache__/metrics_pb2.cpython-37.pyc b/cmd/promgen/__pycache__/metrics_pb2.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c27dbaac451c6dfe9cf80f4ff920538779fc3a8 GIT binary patch literal 3758 zcmb_f&2t<_6`$Gf>HXA5me;a95(`Oe$J$Kng@fPefmRZ)myDnum-SM?HRQWZD}IAx|KP65uC zS&7qtbEYP7#zkJbr3H7k zLW(a{^BP_Kg<@W(g|jNXMz6n2zNksQgc)Os|v54F%sMOEJ1d zbU@3%-6ktBG6?KKRwZ{mMh2+@x*@r>7#ZXypj(o=9V3GnfYv3q5hH_q4Ny&TUyqSN zz5(bp$-N#UgS-Lgj^ysf$RPIsy(zi-F*3*|pt|H5F*3+ofZmqeR*VetO+eq0+&eKc z$hQH#E4lB)$ROp8a`(N{^@nsweJh}3W8|}d+05POg~!L#->?~>^>cCTchxZ>l-s^D z3YZUH<=Gb=G3E-&4~K!{QT&|x++jmO@e{AdTuiLM>RFrumFz&#tSjE z_Hz|wcNjQK{Yz8&TPR0gP;cZ~zT70gWz-j$S#QNs`a5XJ_Nh1mJhil%1U>n!wR+@B zWH|Uz?@oeJdlTh83@wPcOZ5uM{UpZH5p`yja73;B0SxYho@e%%<|BamaD>->9)y@Bp7ssFH=Jn`jUF3FIAL18U`mLN@j*fk^XH8X^$Kpu)c43 zSD3zr^m&KyvCFKWDzvH#-MWu5z0m%c1`uW8)UCw_v@K;8A5ZyJF6kwd;r4)fRy7T> zOZqA*ol>9SVBk|eU@oa6s^a`d>{YF89pgXgPx^srS?b>KTk2M5je(@H{ofXLIs)@~&U1y_5t*Xcfa z*lr$FQu;C~%QbjLrnlQ@;ZtkA-*da{OMQkf zx-!r?>+}4iU@CKr?EHK%LeQ7GKOf8x^ra4PT)m{H?uZ_0Xt};<2q9{5e`p#XS*`<( zBRv#dDRe$<$b0C0K8m_aGqcM)%Nd%P!=vr}y`6D(mmXW88-VYi6O*2w7aP7F=>snV zaRud0@?)0a;{J`7nuK(y?|{-%N*+K@Z4*w1tF0(DSF@VHu5U4IbrNaVXzrMBi^t*GMD6=Fd_cq!2HHsBG*SJ1Nu dnEp~~Moq-OOe&WwBuj}5%$C%givFkHe*h9IKKTFu literal 0 HcmV?d00001 diff --git a/cmd/promgen/metrics.go b/cmd/promgen/metrics.go index 492c45b762525..9c6707c1f942c 100644 --- a/cmd/promgen/metrics.go +++ b/cmd/promgen/metrics.go @@ -152,16 +152,12 @@ func (g *Generator) serveProtobuf(w http.ResponseWriter, r *http.Request) { func (g *Generator) defaultCollector(def MetricDefinition) (prometheus.Collector, error) { switch def.Type { case TypeUntyped: - return prometheus.NewUntypedFunc( + return prometheus.NewUntypedVec( prometheus.UntypedOpts{ - Name: "my_untyped_metric", - Help: "An example of an untyped metric.", - }, - func() float64 { - // This function will be called whenever Prometheus scrapes - // the metric, returning the current value. - return 42.0 // Example value + Name: def.Name, + Help: def.Help, }, + def.Labels, ), nil case TypeCounter: return prometheus.NewCounterVec( diff --git a/cmd/promgen/metrics.proto b/cmd/promgen/metrics.proto new file mode 100644 index 0000000000000..3e9168e66a5c4 --- /dev/null +++ b/cmd/promgen/metrics.proto @@ -0,0 +1,157 @@ +// Copyright 2013 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto2"; + +package io.prometheus.client; +option java_package = "io.prometheus.client"; +option go_package = "github.com/prometheus/client_model/go;io_prometheus_client"; + +import "google/protobuf/timestamp.proto"; + +message LabelPair { + optional string name = 1; + optional string value = 2; +} + +enum MetricType { + // COUNTER must use the Metric field "counter". + COUNTER = 0; + // GAUGE must use the Metric field "gauge". + GAUGE = 1; + // SUMMARY must use the Metric field "summary". + SUMMARY = 2; + // UNTYPED must use the Metric field "untyped". + UNTYPED = 3; + // HISTOGRAM must use the Metric field "histogram". + HISTOGRAM = 4; + // GAUGE_HISTOGRAM must use the Metric field "histogram". + GAUGE_HISTOGRAM = 5; +} + +message Gauge { + optional double value = 1; +} + +message Counter { + optional double value = 1; + optional Exemplar exemplar = 2; + + optional google.protobuf.Timestamp created_timestamp = 3; +} + +message Quantile { + optional double quantile = 1; + optional double value = 2; +} + +message Summary { + optional uint64 sample_count = 1; + optional double sample_sum = 2; + repeated Quantile quantile = 3; + + optional google.protobuf.Timestamp created_timestamp = 4; +} + +message Untyped { + optional double value = 1; +} + +message Histogram { + optional uint64 sample_count = 1; + optional double sample_count_float = 4; // Overrides sample_count if > 0. + optional double sample_sum = 2; + // Buckets for the conventional histogram. + repeated Bucket bucket = 3; // Ordered in increasing order of upper_bound, +Inf bucket is optional. + + optional google.protobuf.Timestamp created_timestamp = 15; + + // Everything below here is for native histograms (also known as sparse histograms). + // Native histograms are an experimental feature without stability guarantees. + + // schema defines the bucket schema. Currently, valid numbers are -4 <= n <= 8. + // They are all for base-2 bucket schemas, where 1 is a bucket boundary in each case, and + // then each power of two is divided into 2^n logarithmic buckets. + // Or in other words, each bucket boundary is the previous boundary times 2^(2^-n). + // In the future, more bucket schemas may be added using numbers < -4 or > 8. + optional sint32 schema = 5; + optional double zero_threshold = 6; // Breadth of the zero bucket. + optional uint64 zero_count = 7; // Count in zero bucket. + optional double zero_count_float = 8; // Overrides sb_zero_count if > 0. + + // Negative buckets for the native histogram. + repeated BucketSpan negative_span = 9; + // Use either "negative_delta" or "negative_count", the former for + // regular histograms with integer counts, the latter for float + // histograms. + repeated sint64 negative_delta = 10; // Count delta of each bucket compared to previous one (or to zero for 1st bucket). + repeated double negative_count = 11; // Absolute count of each bucket. + + // Positive buckets for the native histogram. + // Use a no-op span (offset 0, length 0) for a native histogram without any + // observations yet and with a zero_threshold of 0. Otherwise, it would be + // indistinguishable from a classic histogram. + repeated BucketSpan positive_span = 12; + // Use either "positive_delta" or "positive_count", the former for + // regular histograms with integer counts, the latter for float + // histograms. + repeated sint64 positive_delta = 13; // Count delta of each bucket compared to previous one (or to zero for 1st bucket). + repeated double positive_count = 14; // Absolute count of each bucket. + + // Only used for native histograms. These exemplars MUST have a timestamp. + repeated Exemplar exemplars = 16; +} + +// A Bucket of a conventional histogram, each of which is treated as +// an individual counter-like time series by Prometheus. +message Bucket { + optional uint64 cumulative_count = 1; // Cumulative in increasing order. + optional double cumulative_count_float = 4; // Overrides cumulative_count if > 0. + optional double upper_bound = 2; // Inclusive. + optional Exemplar exemplar = 3; +} + +// A BucketSpan defines a number of consecutive buckets in a native +// histogram with their offset. Logically, it would be more +// straightforward to include the bucket counts in the Span. However, +// the protobuf representation is more compact in the way the data is +// structured here (with all the buckets in a single array separate +// from the Spans). +message BucketSpan { + optional sint32 offset = 1; // Gap to previous span, or starting point for 1st span (which can be negative). + optional uint32 length = 2; // Length of consecutive buckets. +} + +message Exemplar { + repeated LabelPair label = 1; + optional double value = 2; + optional google.protobuf.Timestamp timestamp = 3; // OpenMetrics-style. +} + +message Metric { + repeated LabelPair label = 1; + optional Gauge gauge = 2; + optional Counter counter = 3; + optional Summary summary = 4; + optional Untyped untyped = 5; + optional Histogram histogram = 7; + optional int64 timestamp_ms = 6; +} + +message MetricFamily { + optional string name = 1; + optional string help = 2; + optional MetricType type = 3; + repeated Metric metric = 4; + optional string unit = 5; +} diff --git a/cmd/promgen/metrics_pb2.py b/cmd/promgen/metrics_pb2.py new file mode 100644 index 0000000000000..c2e274be8b4fe --- /dev/null +++ b/cmd/promgen/metrics_pb2.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: metrics.proto +# Protobuf Python Version: 6.30.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 30, + 2, + '', + 'metrics.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rmetrics.proto\x12\x14io.prometheus.client\x1a\x1fgoogle/protobuf/timestamp.proto\"(\n\tLabelPair\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\x16\n\x05Gauge\x12\r\n\x05value\x18\x01 \x01(\x01\"\x81\x01\n\x07\x43ounter\x12\r\n\x05value\x18\x01 \x01(\x01\x12\x30\n\x08\x65xemplar\x18\x02 \x01(\x0b\x32\x1e.io.prometheus.client.Exemplar\x12\x35\n\x11\x63reated_timestamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"+\n\x08Quantile\x12\x10\n\x08quantile\x18\x01 \x01(\x01\x12\r\n\x05value\x18\x02 \x01(\x01\"\x9c\x01\n\x07Summary\x12\x14\n\x0csample_count\x18\x01 \x01(\x04\x12\x12\n\nsample_sum\x18\x02 \x01(\x01\x12\x30\n\x08quantile\x18\x03 \x03(\x0b\x32\x1e.io.prometheus.client.Quantile\x12\x35\n\x11\x63reated_timestamp\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\x18\n\x07Untyped\x12\r\n\x05value\x18\x01 \x01(\x01\"\x91\x04\n\tHistogram\x12\x14\n\x0csample_count\x18\x01 \x01(\x04\x12\x1a\n\x12sample_count_float\x18\x04 \x01(\x01\x12\x12\n\nsample_sum\x18\x02 \x01(\x01\x12,\n\x06\x62ucket\x18\x03 \x03(\x0b\x32\x1c.io.prometheus.client.Bucket\x12\x35\n\x11\x63reated_timestamp\x18\x0f \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06schema\x18\x05 \x01(\x11\x12\x16\n\x0ezero_threshold\x18\x06 \x01(\x01\x12\x12\n\nzero_count\x18\x07 \x01(\x04\x12\x18\n\x10zero_count_float\x18\x08 \x01(\x01\x12\x37\n\rnegative_span\x18\t \x03(\x0b\x32 .io.prometheus.client.BucketSpan\x12\x16\n\x0enegative_delta\x18\n \x03(\x12\x12\x16\n\x0enegative_count\x18\x0b \x03(\x01\x12\x37\n\rpositive_span\x18\x0c \x03(\x0b\x32 .io.prometheus.client.BucketSpan\x12\x16\n\x0epositive_delta\x18\r \x03(\x12\x12\x16\n\x0epositive_count\x18\x0e \x03(\x01\x12\x31\n\texemplars\x18\x10 \x03(\x0b\x32\x1e.io.prometheus.client.Exemplar\"\x89\x01\n\x06\x42ucket\x12\x18\n\x10\x63umulative_count\x18\x01 \x01(\x04\x12\x1e\n\x16\x63umulative_count_float\x18\x04 \x01(\x01\x12\x13\n\x0bupper_bound\x18\x02 \x01(\x01\x12\x30\n\x08\x65xemplar\x18\x03 \x01(\x0b\x32\x1e.io.prometheus.client.Exemplar\",\n\nBucketSpan\x12\x0e\n\x06offset\x18\x01 \x01(\x11\x12\x0e\n\x06length\x18\x02 \x01(\r\"x\n\x08\x45xemplar\x12.\n\x05label\x18\x01 \x03(\x0b\x32\x1f.io.prometheus.client.LabelPair\x12\r\n\x05value\x18\x02 \x01(\x01\x12-\n\ttimestamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xbe\x02\n\x06Metric\x12.\n\x05label\x18\x01 \x03(\x0b\x32\x1f.io.prometheus.client.LabelPair\x12*\n\x05gauge\x18\x02 \x01(\x0b\x32\x1b.io.prometheus.client.Gauge\x12.\n\x07\x63ounter\x18\x03 \x01(\x0b\x32\x1d.io.prometheus.client.Counter\x12.\n\x07summary\x18\x04 \x01(\x0b\x32\x1d.io.prometheus.client.Summary\x12.\n\x07untyped\x18\x05 \x01(\x0b\x32\x1d.io.prometheus.client.Untyped\x12\x32\n\thistogram\x18\x07 \x01(\x0b\x32\x1f.io.prometheus.client.Histogram\x12\x14\n\x0ctimestamp_ms\x18\x06 \x01(\x03\"\x96\x01\n\x0cMetricFamily\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04help\x18\x02 \x01(\t\x12.\n\x04type\x18\x03 \x01(\x0e\x32 .io.prometheus.client.MetricType\x12,\n\x06metric\x18\x04 \x03(\x0b\x32\x1c.io.prometheus.client.Metric\x12\x0c\n\x04unit\x18\x05 \x01(\t*b\n\nMetricType\x12\x0b\n\x07\x43OUNTER\x10\x00\x12\t\n\x05GAUGE\x10\x01\x12\x0b\n\x07SUMMARY\x10\x02\x12\x0b\n\x07UNTYPED\x10\x03\x12\r\n\tHISTOGRAM\x10\x04\x12\x13\n\x0fGAUGE_HISTOGRAM\x10\x05\x42R\n\x14io.prometheus.clientZ:github.com/prometheus/client_model/go;io_prometheus_client') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'metrics_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\024io.prometheus.clientZ:github.com/prometheus/client_model/go;io_prometheus_client' + _globals['_METRICTYPE']._serialized_start=1814 + _globals['_METRICTYPE']._serialized_end=1912 + _globals['_LABELPAIR']._serialized_start=72 + _globals['_LABELPAIR']._serialized_end=112 + _globals['_GAUGE']._serialized_start=114 + _globals['_GAUGE']._serialized_end=136 + _globals['_COUNTER']._serialized_start=139 + _globals['_COUNTER']._serialized_end=268 + _globals['_QUANTILE']._serialized_start=270 + _globals['_QUANTILE']._serialized_end=313 + _globals['_SUMMARY']._serialized_start=316 + _globals['_SUMMARY']._serialized_end=472 + _globals['_UNTYPED']._serialized_start=474 + _globals['_UNTYPED']._serialized_end=498 + _globals['_HISTOGRAM']._serialized_start=501 + _globals['_HISTOGRAM']._serialized_end=1030 + _globals['_BUCKET']._serialized_start=1033 + _globals['_BUCKET']._serialized_end=1170 + _globals['_BUCKETSPAN']._serialized_start=1172 + _globals['_BUCKETSPAN']._serialized_end=1216 + _globals['_EXEMPLAR']._serialized_start=1218 + _globals['_EXEMPLAR']._serialized_end=1338 + _globals['_METRIC']._serialized_start=1341 + _globals['_METRIC']._serialized_end=1659 + _globals['_METRICFAMILY']._serialized_start=1662 + _globals['_METRICFAMILY']._serialized_end=1812 +# @@protoc_insertion_point(module_scope) diff --git a/cmd/promgen/proto-reader.py b/cmd/promgen/proto-reader.py new file mode 100644 index 0000000000000..cf7def5c5a077 --- /dev/null +++ b/cmd/promgen/proto-reader.py @@ -0,0 +1,32 @@ +import sys +from google.protobuf import text_format +# The following import will be based on your compiled proto file name +# For example, if your proto file is named "message.proto", it will generate "message_pb2.py" +import metrics_pb2 + +def read_proto_binary(binary_file_path, proto_class): + # Create an instance of your message class + message = proto_class() + + # Read the binary file + with open(binary_file_path, 'rb') as f: + message.ParseFromString(f.read()) + + # Print the message in human-readable format + print(text_format.MessageToString(message)) + +def main(): + if len(sys.argv) != 2: + print("Usage: python script.py ") + sys.exit(1) + + binary_file_path = sys.argv[1] + + try: + # Replace YourProtoClass with the actual class name from your compiled proto + read_proto_binary(binary_file_path, YourProtoClass) + except Exception as e: + print(f"Error: {e}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/cmd/promgen/protobuf.bin b/cmd/promgen/protobuf.bin new file mode 100644 index 0000000000000000000000000000000000000000..474700965489a4352a0eef7814db8ae23f0aa8d0 GIT binary patch literal 879 zcmd-QPOT`&&r8iK$xO_NFD}i^O)RPuvUF5{G8M`*@{3ay$`W%*Q;QYS@^f@HORYe97)i_%6$t96VVx7`KhPMwPmXMf}%#m&JW zSSZLqUoNMhme7hnXEYyzxc(m8`Eg8_({!6b;q z2!t#^9s`hM0g8)c2xkaDWqH7=Wio_-e2xqaAePT?0%ETWiwu(t?Tnxd(Tt)Dy$miO zOEM!X!v-kY#+VTaG)5Yz+!CnNHA6kaG@~-Z9H=}Vh}|>vfl5_@2J>cc10m3IctG8n za1|I(g1Py5`6c;znaT0V`K5U!sYOBxjtWqwLPDWEh?BGXM%~kQ$1d*&0O_V5p#T5? literal 0 HcmV?d00001 diff --git a/share/testdata/histograms/generator.go b/share/testdata/histograms/generator.go new file mode 100644 index 0000000000000..1cc3e374cdd02 --- /dev/null +++ b/share/testdata/histograms/generator.go @@ -0,0 +1,266 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package histograms + +import ( + "math" + "math/rand/v2" + "sort" +) + +type DistributionConfig struct { + Name string + SampleSize int + Params map[string]float64 +} + +type GeneratedDataset struct { + Name string + Data []float64 + ActualPercentiles map[float64]float64 + Histograms []HistogramTestCase +} + +func GenerateTestData() []GeneratedDataset { + return GenerateTestDataWithSeed(42) +} + +func GenerateTestDataWithSeed(seed uint64) []GeneratedDataset { + + configs := []DistributionConfig{ + {Name: "LogNormal", SampleSize: 2000, Params: map[string]float64{"mu": 2.0, "sigma": 0.5}}, + {Name: "Weibull", SampleSize: 2000, Params: map[string]float64{"shape": 2.0, "scale": 100.0}}, + {Name: "Normal", SampleSize: 2000, Params: map[string]float64{"mean": 50.0, "stddev": 15.0}}, + {Name: "Gamma", SampleSize: 2000, Params: map[string]float64{"shape": 2.0, "rate": 0.1}}, + } + + var datasets []GeneratedDataset + for _, config := range configs { + // each dataset gets a copy of the same rand so that they don't interfere with each other + rng := rand.New(rand.NewPCG(seed, seed)) + data := generateSamples(config, rng) + percentiles := calculatePercentiles(data, []float64{0.01, 0.25, 0.5, 0.75, 0.99}) + histograms := createHistograms(config.Name, data, percentiles) + + datasets = append(datasets, GeneratedDataset{ + Name: config.Name, + Data: data, + ActualPercentiles: percentiles, + Histograms: histograms, + }) + } + + return datasets +} + +func generateSamples(config DistributionConfig, rng *rand.Rand) []float64 { + data := make([]float64, config.SampleSize) + + switch config.Name { + case "LogNormal": + mu, sigma := config.Params["mu"], config.Params["sigma"] + for i := 0; i < config.SampleSize; i++ { + data[i] = math.Exp(rng.NormFloat64()*sigma + mu) + } + case "Weibull": + shape, scale := config.Params["shape"], config.Params["scale"] + for i := 0; i < config.SampleSize; i++ { + u := rng.Float64() + data[i] = scale * math.Pow(-math.Log(1-u), 1/shape) + } + case "Normal": + mean, stddev := config.Params["mean"], config.Params["stddev"] + for i := 0; i < config.SampleSize; i++ { + data[i] = rng.NormFloat64()*stddev + mean + } + case "Gamma": + shape, rate := config.Params["shape"], config.Params["rate"] + for i := 0; i < config.SampleSize; i++ { + data[i] = gammaRandom(shape, rng) / rate + } + } + + return data +} + +func gammaRandom(shape float64, rng *rand.Rand) float64 { + if shape < 1 { + return gammaRandom(shape+1, rng) * math.Pow(rng.Float64(), 1/shape) + } + + d := shape - 1.0/3.0 + c := 1.0 / math.Sqrt(9.0*d) + + for { + x := rng.NormFloat64() + v := 1.0 + c*x + if v <= 0 { + continue + } + v = v * v * v + u := rng.Float64() + if u < 1.0-0.0331*(x*x)*(x*x) { + return d * v + } + if math.Log(u) < 0.5*x*x+d*(1.0-v+math.Log(v)) { + return d * v + } + } +} + +func calculatePercentiles(data []float64, percentiles []float64) map[float64]float64 { + sorted := make([]float64, len(data)) + copy(sorted, data) + sort.Float64s(sorted) + + result := make(map[float64]float64) + for _, p := range percentiles { + idx := int(p * float64(len(sorted)-1)) + result[p] = sorted[idx] + } + return result +} + +func createHistograms(name string, data []float64, percentiles map[float64]float64) []HistogramTestCase { + min, max := minMax(data) + sum := sum(data) + + // Create different histogram configurations + configs := []struct { + suffix string + boundaries []float64 + }{ + {"Linear10", createLinearBoundaries(min, max, 10)}, + {"Linear20", createLinearBoundaries(min, max, 20)}, + {"Exponential", createExponentialBoundaries(min, max, 15)}, + {"Percentile", createPercentileBoundaries(data, []float64{0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.99})}, + } + + var histograms []HistogramTestCase + for _, config := range configs { + counts := calculateCounts(data, config.boundaries) + + histograms = append(histograms, HistogramTestCase{ + Name: name + "_" + config.suffix, + Input: HistogramInput{ + Count: uint64(len(data)), + Sum: sum, + Min: &min, + Max: &max, + Boundaries: config.boundaries, + Counts: counts, + Attributes: map[string]string{"distribution": name, "config": config.suffix}, + }, + Expected: ExpectedMetrics{ + Count: uint64(len(data)), + Sum: sum, + Average: sum / float64(len(data)), + Min: &min, + Max: &max, + PercentileRanges: createPercentileRanges(percentiles, config.boundaries, min, max), + }, + }) + } + + return histograms +} + +func createLinearBoundaries(min, max float64, buckets int) []float64 { + boundaries := make([]float64, buckets-1) + step := (max - min) / float64(buckets) + for i := 0; i < buckets-1; i++ { + boundaries[i] = min + float64(i+1)*step + } + return boundaries +} + +func createExponentialBoundaries(min, max float64, buckets int) []float64 { + if min <= 0 { + min = 0.1 + } + boundaries := make([]float64, buckets-1) + ratio := math.Pow(max/min, 1.0/float64(buckets)) + for i := 0; i < buckets-1; i++ { + boundaries[i] = min * math.Pow(ratio, float64(i+1)) + } + return boundaries +} + +func createPercentileBoundaries(data []float64, percentiles []float64) []float64 { + sorted := make([]float64, len(data)) + copy(sorted, data) + sort.Float64s(sorted) + + boundaries := make([]float64, len(percentiles)) + for i, p := range percentiles { + idx := int(p * float64(len(sorted)-1)) + boundaries[i] = sorted[idx] + } + return boundaries +} + +func calculateCounts(data []float64, boundaries []float64) []uint64 { + counts := make([]uint64, len(boundaries)+1) + + for _, value := range data { + bucket := 0 + for i, boundary := range boundaries { + if value <= boundary { + bucket = i + break + } + bucket = i + 1 + } + counts[bucket]++ + } + + return counts +} + +func createPercentileRanges(percentiles map[float64]float64, boundaries []float64, min, max float64) map[float64]PercentileRange { + ranges := make(map[float64]PercentileRange) + + for p, value := range percentiles { + low, high := min, max + + // Find bucket containing this percentile value + for i, boundary := range boundaries { + if value <= boundary { + if i > 0 { + low = boundaries[i-1] + } + high = boundary + break + } + if i == len(boundaries)-1 { + low = boundary + } + } + + ranges[p] = PercentileRange{Low: low, High: high} + } + + return ranges +} + +func minMax(data []float64) (float64, float64) { + min, max := data[0], data[0] + for _, v := range data[1:] { + if v < min { + min = v + } + if v > max { + max = v + } + } + return min, max +} + +func sum(data []float64) float64 { + total := 0.0 + for _, v := range data { + total += v + } + return total +} diff --git a/share/testdata/histograms/generator_test.go b/share/testdata/histograms/generator_test.go new file mode 100644 index 0000000000000..c6ca006fbc08f --- /dev/null +++ b/share/testdata/histograms/generator_test.go @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package histograms + +import ( + "testing" + + "github.com/amazon-contributing/opentelemetry-collector-contrib/share/testdata/histograms" +) + +func TestGenerate(t *testing.T) { + datasets := histograms.GenerateTestDataWithSeed(12345) + + for _, ds := range datasets { + + } +} diff --git a/share/testdata/histograms/histograms.go b/share/testdata/histograms/histograms.go index 55290048d8987..399124455bede 100644 --- a/share/testdata/histograms/histograms.go +++ b/share/testdata/histograms/histograms.go @@ -324,6 +324,19 @@ func TestCases() []HistogramTestCase { }, Expected: ExpectedMetrics{ Count: 101, + Sum: 200, + Average: 1.98, + Min: ptr(-100.0), + Max: ptr(60.0), + PercentileRanges: map[float64]PercentileRange{}, + }, + }, + } +} + +func ptr(f float64) *float64 { + return &f +}101, Sum: -3000, Average: -29.70, Min: ptr(-100.0), @@ -470,7 +483,7 @@ func TestCases() []HistogramTestCase { Min: nil, Max: nil, Boundaries: []float64{}, - Counts: []uint64{}, + Counts: []uint64{75}, Attributes: map[string]string{"service.name": "unbounded-service"}, }, Expected: ExpectedMetrics{ diff --git a/share/testdata/histograms/histograms_test.go b/share/testdata/histograms/histograms_test.go index f6ea357921224..3ab9d1f15acff 100644 --- a/share/testdata/histograms/histograms_test.go +++ b/share/testdata/histograms/histograms_test.go @@ -46,7 +46,7 @@ func TestInvalidHistogramFeasibility(t *testing.T) { func TestVisualizeHistograms(t *testing.T) { // comment the next line to visualize the input histograms - t.Skip("Skip visualization test") + //t.Skip("Skip visualization test") testCases := TestCases() for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { From 8e2723f6df967ff70246cdfdd397fc072b3652b9 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Fri, 19 Sep 2025 16:11:51 -0400 Subject: [PATCH 06/23] Fix test case expected values --- share/testdata/histograms/histograms.go | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/share/testdata/histograms/histograms.go b/share/testdata/histograms/histograms.go index 399124455bede..46d45c766f73e 100644 --- a/share/testdata/histograms/histograms.go +++ b/share/testdata/histograms/histograms.go @@ -224,7 +224,7 @@ func TestCases() []HistogramTestCase { Attributes: map[string]string{"service.name": "detailed-metrics"}, }, Expected: ExpectedMetrics{ - Count: 1111, + Count: 1124, Sum: 350000, Average: 315.03, Min: ptr(0.5), @@ -281,7 +281,7 @@ func TestCases() []HistogramTestCase { }, Expected: ExpectedMetrics{ Count: 101, - Sum: -6000, + Sum: -10000, Average: -59.41, Min: ptr(-200.0), Max: ptr(-10.0), @@ -301,7 +301,7 @@ func TestCases() []HistogramTestCase { Attributes: map[string]string{"service.name": "temperature-service"}, }, Expected: ExpectedMetrics{ - Count: 101, + Count: 106, Sum: 0, Average: 0.0, Min: ptr(-50.0), @@ -325,19 +325,6 @@ func TestCases() []HistogramTestCase { Expected: ExpectedMetrics{ Count: 101, Sum: 200, - Average: 1.98, - Min: ptr(-100.0), - Max: ptr(60.0), - PercentileRanges: map[float64]PercentileRange{}, - }, - }, - } -} - -func ptr(f float64) *float64 { - return &f -}101, - Sum: -3000, Average: -29.70, Min: ptr(-100.0), Max: ptr(60.0), @@ -358,7 +345,7 @@ func ptr(f float64) *float64 { }, Expected: ExpectedMetrics{ Count: 100, - Sum: 10000, + Sum: 8000, Average: 1000, Min: ptr(10.0), Max: ptr(160.0), @@ -542,7 +529,7 @@ func ptr(f float64) *float64 { }, Expected: ExpectedMetrics{ Count: 1936, - Sum: 1557000, + Sum: 1697000, Average: 804.23, Min: ptr(5.0), Max: ptr(1800.0), From 4c7a477736afcb829c004572ddeef8bcdfc82bba Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Tue, 14 Oct 2025 13:39:31 -0400 Subject: [PATCH 07/23] Make generator runnable --- cmd/generator/{ => generator}/README.md | 0 .../{ => generator}/distributions.go | 0 cmd/generator/{ => generator}/example_test.go | 17 +++ cmd/generator/{ => generator}/generator.go | 0 .../{ => generator}/histogram_generator.go | 0 .../{ => generator}/otlp_publisher.go | 0 cmd/generator/{ => generator}/types.go | 0 cmd/generator/go.mod | 41 ++++--- cmd/generator/go.sum | 108 ++++++++++++------ cmd/generator/main.go | 43 +++++++ 10 files changed, 150 insertions(+), 59 deletions(-) rename cmd/generator/{ => generator}/README.md (100%) rename cmd/generator/{ => generator}/distributions.go (100%) rename cmd/generator/{ => generator}/example_test.go (98%) rename cmd/generator/{ => generator}/generator.go (100%) rename cmd/generator/{ => generator}/histogram_generator.go (100%) rename cmd/generator/{ => generator}/otlp_publisher.go (100%) rename cmd/generator/{ => generator}/types.go (100%) create mode 100644 cmd/generator/main.go diff --git a/cmd/generator/README.md b/cmd/generator/generator/README.md similarity index 100% rename from cmd/generator/README.md rename to cmd/generator/generator/README.md diff --git a/cmd/generator/distributions.go b/cmd/generator/generator/distributions.go similarity index 100% rename from cmd/generator/distributions.go rename to cmd/generator/generator/distributions.go diff --git a/cmd/generator/example_test.go b/cmd/generator/generator/example_test.go similarity index 98% rename from cmd/generator/example_test.go rename to cmd/generator/generator/example_test.go index 47064a3656f19..36b58174693bf 100644 --- a/cmd/generator/example_test.go +++ b/cmd/generator/generator/example_test.go @@ -6,6 +6,7 @@ package generator_test import ( "fmt" "math/rand" + "slices" "testing" "time" @@ -618,6 +619,22 @@ func TestEdgeCases(t *testing.T) { }) } +func TestGenerateAccuracyDataset(t *testing.T) { + rng := rand.New(rand.NewSource(0xFEEDBEEF)) + datapoints := make([]float64, 10000) + for i := range 10000 { + datapoints[i] = generator.LogNormalRandom(rng, -4.894, 1.176) + } + slices.Sort(datapoints) + for i, v := range datapoints { + if i > 0 { + fmt.Print(", ") + } + fmt.Printf("%.3e", v) + } + fmt.Println() +} + // Helper function for absolute value func abs(x float64) float64 { if x < 0 { diff --git a/cmd/generator/generator.go b/cmd/generator/generator/generator.go similarity index 100% rename from cmd/generator/generator.go rename to cmd/generator/generator/generator.go diff --git a/cmd/generator/histogram_generator.go b/cmd/generator/generator/histogram_generator.go similarity index 100% rename from cmd/generator/histogram_generator.go rename to cmd/generator/generator/histogram_generator.go diff --git a/cmd/generator/otlp_publisher.go b/cmd/generator/generator/otlp_publisher.go similarity index 100% rename from cmd/generator/otlp_publisher.go rename to cmd/generator/generator/otlp_publisher.go diff --git a/cmd/generator/types.go b/cmd/generator/generator/types.go similarity index 100% rename from cmd/generator/types.go rename to cmd/generator/generator/types.go diff --git a/cmd/generator/go.mod b/cmd/generator/go.mod index 23649835c206a..b13b65c58f57b 100644 --- a/cmd/generator/go.mod +++ b/cmd/generator/go.mod @@ -2,34 +2,33 @@ module github.com/amazon-contributing/opentelemetry-collector-contrib/cmd/genera go 1.24.7 -require ( - github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 - go.opentelemetry.io/otel/metric v1.37.0 - go.opentelemetry.io/otel/sdk/metric v1.37.0 -) +require github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.137.0 require ( - github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect + github.com/lightstep/go-expohisto v1.0.0 // indirect github.com/spf13/pflag v1.0.10 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.37.0 // indirect - go.opentelemetry.io/otel/sdk v1.37.0 // indirect - go.opentelemetry.io/otel/trace v1.37.0 // indirect - go.opentelemetry.io/proto/otlp v1.7.1 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/proto/otlp v1.8.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/net v0.42.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/time v0.12.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 // indirect - google.golang.org/grpc v1.75.0 // indirect - google.golang.org/protobuf v1.36.8 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.13.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/grpc v1.75.1 // indirect + google.golang.org/protobuf v1.36.9 // indirect ) diff --git a/cmd/generator/go.sum b/cmd/generator/go.sum index 08edfdd2b9f6e..1195aaa9f6af1 100644 --- a/cmd/generator/go.sum +++ b/cmd/generator/go.sum @@ -1,53 +1,85 @@ -github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= -github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= -github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0 h1:/PSf7CIVu//VV7zYeYhnOLIgMsrONH37XV2mzeVtjZk= -github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0/go.mod h1:RpbRtcf6cXpgn8aJOf6SvIuwGo2ycRUEI2HASamCjlw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/lightstep/go-expohisto v1.0.0 h1:UPtTS1rGdtehbbAF7o/dhkWLTDI73UifG8LbfQI7cA4= +github.com/lightstep/go-expohisto v1.0.0/go.mod h1:xDXD0++Mu2FOaItXtdDfksfgxfV0z1TMPa+e/EUd0cs= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.137.0 h1:+rU6PWPc7Jcy39hnf08pHq6DsDhqpF2NYpMFPUKEIgw= +github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.137.0/go.mod h1:Ll36/tgcetzSkSQR4D9bFcCEXLqGvr82vuhuxoh7h2Q= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 h1:zG8GlgXCJQd5BU98C0hZnBbElszTmUgCNCfYneaDL0A= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0/go.mod h1:hOfBCz8kv/wuq73Mx2H2QnWokh/kHZxkh6SNF2bdKtw= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 h1:9PgnL3QNlj10uGxExowIDIZu66aVBwWhXmbOp1pa6RA= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0/go.mod h1:0ineDcLELf6JmKfuo0wvvhAVMuxWFYvkTin2iV4ydPQ= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= -go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= -go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= -go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= -go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= -go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= +go.opentelemetry.io/collector/featuregate v1.43.0 h1:Aq8UR5qv1zNlbbkTyqv8kLJtnoQMq/sG1/jS9o1cCJI= +go.opentelemetry.io/collector/featuregate v1.43.0/go.mod h1:d0tiRzVYrytB6LkcYgz2ESFTv7OktRPQe0QEQcPt1L4= +go.opentelemetry.io/collector/pdata v1.43.0 h1:zVkj2hcjiMLwX+QDDNwb7iTh3LBjNXKv2qPSgj1Rzb4= +go.opentelemetry.io/collector/pdata v1.43.0/go.mod h1:KsJzdDG9e5BaHlmYr0sqdSEKeEiSfKzoF+rdWU7J//w= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE= +go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= -google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0 h1:0UOBWO4dC+e51ui0NFKSPbkHHiQ4TmrEfEZMLDyRmY8= -google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0/go.mod h1:8ytArBbtOy2xfht+y2fqKd5DRDJRUQhqbyEnQ4bDChs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 h1:MAKi5q709QWfnkkpNQ0M12hYJ1+e8qYVDyowc4U1XZM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= -google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= +golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cmd/generator/main.go b/cmd/generator/main.go new file mode 100644 index 0000000000000..baf8739d2c8d5 --- /dev/null +++ b/cmd/generator/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "log" + "math/rand" + "time" + + "github.com/amazon-contributing/opentelemetry-collector-contrib/cmd/generator/generator" +) + +func main() { + gen := generator.NewHistogramGenerator(generator.GenerationOptions{ + Seed: time.Now().UnixNano(), + Endpoint: "localhost:4318", // OTLP HTTP endpoint + }) + + ticker := time.NewTicker(time.Second * 10) + for range ticker.C { + _, err := gen.GenerateAndPublishHistograms( + generator.HistogramInput{ + Min: ptr(0), + Max: ptr(100), + Count: 1000, + Boundaries: []float64{ + 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, + }, + Attributes: map[string]string{ + "service.name": "test-service", + "operation": "test-operation", + }, + }, func(rand *rand.Rand, time time.Time) float64 { + return generator.SinusoidalValue(rand, time, 100, 0.1, 0, 1) + }) + if err != nil { + log.Printf("Error generating histograms: %v\n", err) + } + } + +} + +func ptr(f float64) *float64 { + return &f +} From 6bbdb46b858cc574cd090d6e489af7a40088fad0 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Tue, 14 Oct 2025 15:46:27 -0400 Subject: [PATCH 08/23] Use otel SDK directly for metric gen --- cmd/generator/go.mod | 16 +-- cmd/generator/main.go | 257 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 247 insertions(+), 26 deletions(-) diff --git a/cmd/generator/go.mod b/cmd/generator/go.mod index b13b65c58f57b..1656b486df892 100644 --- a/cmd/generator/go.mod +++ b/cmd/generator/go.mod @@ -2,7 +2,15 @@ module github.com/amazon-contributing/opentelemetry-collector-contrib/cmd/genera go 1.24.7 -require github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.137.0 +require ( + github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.137.0 + go.opentelemetry.io/otel v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 + go.opentelemetry.io/otel/sdk v1.38.0 + go.opentelemetry.io/otel/sdk/metric v1.38.0 + go.uber.org/zap v1.27.0 + golang.org/x/time v0.13.0 +) require ( github.com/cenkalti/backoff/v5 v5.0.3 // indirect @@ -13,20 +21,14 @@ require ( github.com/lightstep/go-expohisto v1.0.0 // indirect github.com/spf13/pflag v1.0.10 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/sdk v1.38.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.8.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/text v0.28.0 // indirect - golang.org/x/time v0.13.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/grpc v1.75.1 // indirect diff --git a/cmd/generator/main.go b/cmd/generator/main.go index baf8739d2c8d5..be58190af4fb0 100644 --- a/cmd/generator/main.go +++ b/cmd/generator/main.go @@ -1,41 +1,260 @@ package main import ( + "context" + "errors" + "fmt" "log" "math/rand" + "strconv" + "strings" "time" "github.com/amazon-contributing/opentelemetry-collector-contrib/cmd/generator/generator" + types "github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/pkg" + "github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/pkg/metrics" + "github.com/spf13/pflag" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" + "go.uber.org/zap" + "golang.org/x/time/rate" ) +const ( + defaultGRPCEndpoint = "localhost:4317" + defaultHTTPEndpoint = "localhost:4318" +) + +var ( + errFormatOTLPAttributes = errors.New("value should be in one of the following formats: key=\"value\", key=true, key=false, or key=") + errDoubleQuotesOTLPAttributes = errors.New("value should be a string wrapped in double quotes") +) + +type KeyValue map[string]any + +var _ pflag.Value = (*KeyValue)(nil) + +func (*KeyValue) String() string { + return "" +} + +func (v *KeyValue) Set(s string) error { + kv := strings.SplitN(s, "=", 2) + if len(kv) != 2 { + return errFormatOTLPAttributes + } + val := kv[1] + if val == "true" { + (*v)[kv[0]] = true + return nil + } + if val == "false" { + (*v)[kv[0]] = false + return nil + } + if intVal, err := strconv.Atoi(val); err == nil { + (*v)[kv[0]] = intVal + return nil + } + if len(val) < 2 || !strings.HasPrefix(val, "\"") || !strings.HasSuffix(val, "\"") { + return errDoubleQuotesOTLPAttributes + } + + (*v)[kv[0]] = val[1 : len(val)-1] + return nil +} + +func (*KeyValue) Type() string { + return "map[string]any" +} + +type ClientAuth struct { + Enabled bool + ClientCertFile string + ClientKeyFile string +} + +// Config describes the test scenario. +type Config struct { + WorkerCount int + Rate float64 + TotalDuration types.DurationWithInf + ReportingInterval time.Duration + SkipSettingGRPCLogger bool + + // OTLP config + CustomEndpoint string + Insecure bool + InsecureSkipVerify bool + UseHTTP bool + HTTPPath string + Headers KeyValue + ResourceAttributes KeyValue + ServiceName string + TelemetryAttributes KeyValue + + // OTLP TLS configuration + CaFile string + + // OTLP mTLS configuration + ClientAuth ClientAuth + + // Export behavior configuration + AllowExportFailures bool + + // Load testing configuration + LoadSize int + + NumMetrics int + MetricName string + MetricType metrics.MetricType + AggregationTemporality metricdata.Temporality + SpanID string + TraceID string + EnforceUniqueTimeseries bool + UniqueTimelimit time.Duration +} + +// Endpoint returns the appropriate endpoint URL based on the selected communication mode (gRPC or HTTP) +// or custom endpoint provided in the configuration. +func (c *Config) Endpoint() string { + if c.CustomEndpoint != "" { + return c.CustomEndpoint + } + if c.UseHTTP { + return defaultHTTPEndpoint + } + return defaultGRPCEndpoint +} + +func (c *Config) GetHeaders() map[string]string { + m := make(map[string]string, len(c.Headers)) + + for k, t := range c.Headers { + switch v := t.(type) { + case bool: + m[k] = strconv.FormatBool(v) + case string: + m[k] = v + } + } + + return m +} + func main() { + gen := generator.NewHistogramGenerator(generator.GenerationOptions{ - Seed: time.Now().UnixNano(), - Endpoint: "localhost:4318", // OTLP HTTP endpoint + Seed: 12345, // For reproducible results }) - ticker := time.NewTicker(time.Second * 10) - for range ticker.C { - _, err := gen.GenerateAndPublishHistograms( - generator.HistogramInput{ - Min: ptr(0), - Max: ptr(100), - Count: 1000, - Boundaries: []float64{ - 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, - }, - Attributes: map[string]string{ - "service.name": "test-service", - "operation": "test-operation", + input := generator.HistogramInput{ + Count: 1000, + Min: ptr(0.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Attributes: map[string]string{"service.name": "test-service"}, + } + + result, err := gen.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 25) // mean=75, stddev=25 + }) + + exporter, err := createExporter(&Config{ + UseHTTP: true, + Insecure: true, + }) + if err != nil { + log.Fatal(err) + } + + res := resource.NewWithAttributes(semconv.SchemaURL) + limiter := rate.NewLimiter(1, 1) + + startTime := time.Now() + + for { + if err := limiter.Wait(context.Background()); err != nil { + log.Printf("limiter wait failed, retry", zap.Error(err)) + } + + attrs := []attribute.KeyValue{} + for k, v := range result.Input.Attributes { + attrs = append(attrs, attribute.String(k, v)) + } + metrics := []metricdata.Metrics{metricdata.Metrics{ + Name: "CustomOTLPHistogram", + Data: metricdata.Histogram[float64]{ + Temporality: metricdata.DeltaTemporality, + DataPoints: []metricdata.HistogramDataPoint[float64]{ + { + StartTime: startTime, + Time: time.Now(), + Attributes: attribute.NewSet(attrs...), + Count: result.Input.Count, + Sum: result.Input.Sum, + Min: metricdata.NewExtrema[float64](*result.Input.Min), + Max: metricdata.NewExtrema[float64](*result.Input.Max), + Bounds: result.Input.Boundaries, + BucketCounts: result.Input.Counts, + }, }, - }, func(rand *rand.Rand, time time.Time) float64 { - return generator.SinusoidalValue(rand, time, 100, 0.1, 0, 1) - }) + }, + }} + rm := metricdata.ResourceMetrics{ + Resource: res, + ScopeMetrics: []metricdata.ScopeMetrics{{Metrics: metrics}}, + } + + if err := exporter.Export(context.Background(), &rm); err != nil { + log.Fatal("exporter failed", zap.Error(err)) + } + } + +} + +func createExporter(cfg *Config) (sdkmetric.Exporter, error) { + var exp sdkmetric.Exporter + var err error + if cfg.UseHTTP { + var exporterOpts []otlpmetrichttp.Option + + log.Print("starting HTTP exporter") + exporterOpts, err = httpExporterOptions(cfg) if err != nil { - log.Printf("Error generating histograms: %v\n", err) + return nil, err } + exp, err = otlpmetrichttp.New(context.Background(), exporterOpts...) + if err != nil { + return nil, fmt.Errorf("failed to obtain OTLP HTTP exporter: %w", err) + } + } else { + return nil, fmt.Errorf("NotYetImplemented") + } + return exp, err +} + +// httpExporterOptions creates the configuration options for an HTTP-based OTLP metric exporter. +// It configures the exporter with the provided endpoint, URL path, connection security settings, and headers. +func httpExporterOptions(cfg *Config) ([]otlpmetrichttp.Option, error) { + httpExpOpt := []otlpmetrichttp.Option{ + otlpmetrichttp.WithEndpoint(cfg.Endpoint()), + otlpmetrichttp.WithURLPath(cfg.HTTPPath), + } + + if cfg.Insecure { + httpExpOpt = append(httpExpOpt, otlpmetrichttp.WithInsecure()) + } + + if len(cfg.Headers) > 0 { + httpExpOpt = append(httpExpOpt, otlpmetrichttp.WithHeaders(cfg.GetHeaders())) } + return httpExpOpt, nil } func ptr(f float64) *float64 { From 9ce9e722c96896d4897c2d707b11e06ea0e3f980 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Tue, 14 Oct 2025 17:40:51 -0400 Subject: [PATCH 09/23] Use defined test case --- cmd/generator/go.mod | 26 +++++++++++------ cmd/generator/go.sum | 65 +++++++++++++++++++++++++++++++++++-------- cmd/generator/main.go | 39 ++++++++------------------ 3 files changed, 83 insertions(+), 47 deletions(-) diff --git a/cmd/generator/go.mod b/cmd/generator/go.mod index 1656b486df892..a0756ea3c6f41 100644 --- a/cmd/generator/go.mod +++ b/cmd/generator/go.mod @@ -1,9 +1,13 @@ module github.com/amazon-contributing/opentelemetry-collector-contrib/cmd/generator -go 1.24.7 +go 1.25.0 + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/aws => /local/home/dricross/workplace/classichistograms/opentelemetry-collector-contrib/pkg/aws require ( github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.137.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/aws v0.0.0-00010101000000-000000000000 + github.com/spf13/pflag v1.0.10 go.opentelemetry.io/otel v1.38.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 go.opentelemetry.io/otel/sdk v1.38.0 @@ -16,21 +20,27 @@ require ( github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/lightstep/go-expohisto v1.0.0 // indirect - github.com/spf13/pflag v1.0.10 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/collector/featuregate v1.43.0 // indirect + go.opentelemetry.io/collector/pdata v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.8.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/net v0.46.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/grpc v1.75.1 // indirect - google.golang.org/protobuf v1.36.9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251007200510-49b9836ed3ff // indirect + google.golang.org/grpc v1.76.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect ) diff --git a/cmd/generator/go.sum b/cmd/generator/go.sum index 1195aaa9f6af1..322e7e92bef78 100644 --- a/cmd/generator/go.sum +++ b/cmd/generator/go.sum @@ -1,5 +1,6 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -13,6 +14,7 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= @@ -21,10 +23,14 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/lightstep/go-expohisto v1.0.0 h1:UPtTS1rGdtehbbAF7o/dhkWLTDI73UifG8LbfQI7cA4= github.com/lightstep/go-expohisto v1.0.0/go.mod h1:xDXD0++Mu2FOaItXtdDfksfgxfV0z1TMPa+e/EUd0cs= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.137.0 h1:+rU6PWPc7Jcy39hnf08pHq6DsDhqpF2NYpMFPUKEIgw= @@ -33,8 +39,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/collector/featuregate v1.43.0 h1:Aq8UR5qv1zNlbbkTyqv8kLJtnoQMq/sG1/jS9o1cCJI= @@ -57,29 +67,60 @@ go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJr go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE= go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0= +go.opentelemetry.io/proto/slim/otlp v1.8.0 h1:afcLwp2XOeCbGrjufT1qWyruFt+6C9g5SOuymrSPUXQ= +go.opentelemetry.io/proto/slim/otlp v1.8.0/go.mod h1:Yaa5fjYm1SMCq0hG0x/87wV1MP9H5xDuG/1+AhvBcsI= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0 h1:Uc+elixz922LHx5colXGi1ORbsW8DTIGM+gg+D9V7HE= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0/go.mod h1:VyU6dTWBWv6h9w/+DYgSZAPMabWbPTFTuxp25sM8+s0= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0 h1:i8YpvWGm/Uq1koL//bnbJ/26eV3OrKWm09+rDYo7keU= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0/go.mod h1:pQ70xHY/ZVxNUBPn+qUWPl8nwai87eWdqL3M37lNi9A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= -google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= -google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251007200510-49b9836ed3ff h1:A90eA31Wq6HOMIQlLfzFwzqGKBTuaVztYu/g8sn+8Zc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251007200510-49b9836ed3ff/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cmd/generator/main.go b/cmd/generator/main.go index be58190af4fb0..ea0e68bff9b3a 100644 --- a/cmd/generator/main.go +++ b/cmd/generator/main.go @@ -5,14 +5,13 @@ import ( "errors" "fmt" "log" - "math/rand" "strconv" "strings" "time" - "github.com/amazon-contributing/opentelemetry-collector-contrib/cmd/generator/generator" types "github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/pkg" "github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/pkg/metrics" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/aws/cloudwatch/histograms" "github.com/spf13/pflag" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" @@ -148,21 +147,7 @@ func (c *Config) GetHeaders() map[string]string { func main() { - gen := generator.NewHistogramGenerator(generator.GenerationOptions{ - Seed: 12345, // For reproducible results - }) - - input := generator.HistogramInput{ - Count: 1000, - Min: ptr(0.0), - Max: ptr(200.0), - Boundaries: []float64{25, 50, 75, 100, 150}, - Attributes: map[string]string{"service.name": "test-service"}, - } - - result, err := gen.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { - return generator.NormalRandom(rnd, 75, 25) // mean=75, stddev=25 - }) + tc := histograms.TestCases()[0] exporter, err := createExporter(&Config{ UseHTTP: true, @@ -179,15 +164,15 @@ func main() { for { if err := limiter.Wait(context.Background()); err != nil { - log.Printf("limiter wait failed, retry", zap.Error(err)) + log.Print("limiter wait failed, retry", zap.Error(err)) } attrs := []attribute.KeyValue{} - for k, v := range result.Input.Attributes { + for k, v := range tc.Input.Attributes { attrs = append(attrs, attribute.String(k, v)) } - metrics := []metricdata.Metrics{metricdata.Metrics{ - Name: "CustomOTLPHistogram", + metrics := []metricdata.Metrics{{ + Name: tc.Name, Data: metricdata.Histogram[float64]{ Temporality: metricdata.DeltaTemporality, DataPoints: []metricdata.HistogramDataPoint[float64]{ @@ -195,12 +180,12 @@ func main() { StartTime: startTime, Time: time.Now(), Attributes: attribute.NewSet(attrs...), - Count: result.Input.Count, - Sum: result.Input.Sum, - Min: metricdata.NewExtrema[float64](*result.Input.Min), - Max: metricdata.NewExtrema[float64](*result.Input.Max), - Bounds: result.Input.Boundaries, - BucketCounts: result.Input.Counts, + Count: tc.Input.Count, + Sum: tc.Input.Sum, + Min: metricdata.NewExtrema(*tc.Input.Min), + Max: metricdata.NewExtrema(*tc.Input.Max), + Bounds: tc.Input.Boundaries, + BucketCounts: tc.Input.Counts, }, }, }, From 1a1fb0ca3351cd254df3eedbb3a43b69c4860add Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Tue, 14 Oct 2025 17:48:43 -0400 Subject: [PATCH 10/23] move shared files to pkg/aws --- pkg/aws/cloudwatch/histograms/conversion.go | 281 + .../cloudwatch/histograms/conversion_test.go | 290 + .../testdata/exponential/126_Buckets.json | 2506 +++++++ .../testdata/exponential/176_Buckets.json | 3506 +++++++++ .../testdata/exponential/225_Buckets.json | 4506 ++++++++++++ .../testdata/exponential/325_Buckets.json | 6506 +++++++++++++++++ .../testdata/exponential/Basic_Histogram.json | 84 + .../Cumulative_bucket_starts_at_0.json | 204 + .../First_bucket_boundary_equals_minimum.json | 94 + .../testdata/exponential/Large_Numbers.json | 116 + .../testdata/exponential/Many_Buckets.json | 434 ++ .../Negative_and_Positive_Boundaries.json | 78 + .../No_Min_Max_with_Single_Value.json | 8 + .../testdata/exponential/No_Min_or_Max.json | 70 + .../exponential/Only_Max_Defined.json | 90 + .../exponential/Only_Min_Defined.json | 60 + .../exponential/Only_Negative_Boundaries.json | 102 + ...oundaries_but_implied_Negative_Values.json | 102 + .../testdata/exponential/Single_Bucket.json | 8 + .../exponential/Tail_Heavy_Histogram.json | 128 + .../testdata/exponential/Two_Buckets.json | 34 + .../exponential/Unbounded_Histogram.json | 8 + .../exponential/Very_Small_Numbers.json | 92 + .../Zero_Counts_and_Sparse_Data.json | 46 + .../testdata/exponential/lognormal.json | 1058 +++ .../testdata/exponential/weibull.json | 1024 +++ .../histograms/testdata/histogram_mappings.py | 166 + pkg/aws/cloudwatch/tool/cloudwatch_pusher.go | 183 + pkg/aws/cloudwatch/tool/go.mod | 23 + pkg/aws/cloudwatch/tool/go.sum | 28 + pkg/aws/tools/generator.go | 14 - 31 files changed, 21835 insertions(+), 14 deletions(-) create mode 100644 pkg/aws/cloudwatch/histograms/conversion.go create mode 100644 pkg/aws/cloudwatch/histograms/conversion_test.go create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/126_Buckets.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/176_Buckets.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/225_Buckets.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/325_Buckets.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Basic_Histogram.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Cumulative_bucket_starts_at_0.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/First_bucket_boundary_equals_minimum.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Large_Numbers.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Many_Buckets.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Negative_and_Positive_Boundaries.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_Max_with_Single_Value.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_or_Max.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Max_Defined.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Min_Defined.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Negative_Boundaries.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Positive_boundaries_but_implied_Negative_Values.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Single_Bucket.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Tail_Heavy_Histogram.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Two_Buckets.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Unbounded_Histogram.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Very_Small_Numbers.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Zero_Counts_and_Sparse_Data.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/lognormal.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/weibull.json create mode 100644 pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py create mode 100644 pkg/aws/cloudwatch/tool/cloudwatch_pusher.go create mode 100644 pkg/aws/cloudwatch/tool/go.mod create mode 100644 pkg/aws/cloudwatch/tool/go.sum diff --git a/pkg/aws/cloudwatch/histograms/conversion.go b/pkg/aws/cloudwatch/histograms/conversion.go new file mode 100644 index 0000000000000..dc64f9023a8f5 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/conversion.go @@ -0,0 +1,281 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package histograms // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/aws/cloudwatch/histograms" + +import ( + "math" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/aws/cloudwatch" + "go.opentelemetry.io/collector/pdata/pmetric" +) + +type ExponentialMapping struct { + maximum float64 + minimum float64 + sampleCount float64 + sum float64 + values []float64 + counts []float64 +} + +var _ (cloudwatch.HistogramDataPoint) = (*ExponentialMapping)(nil) + +// ConvertOTelToCloudWatch converts an OpenTelemetry histogram datapoint to a CloudWatch histogram datapoint using +// exponential mapping +func ConvertOTelToCloudWatch(dp pmetric.HistogramDataPoint) cloudwatch.HistogramDataPoint { + // maximumInnerBucketCount is the maximum number of inner buckets that each outer bucket can be represented with + // + // A larger values increase the resolution at which the data is sub-sampled while also incurring additional memory + // allocation, processing time, and the maximum number of value/count pairs that are sent to CloudWatch which could + // cause a CloudWatch PutMetricData / PutLogEvent request to be split into multiple requests due to the 100/150 + // metric datapoint limit. + const maximumInnerBucketCount = 10 + + // No validations - assuming valid input histogram + + em := &ExponentialMapping{ + maximum: dp.Max(), + minimum: dp.Min(), + sampleCount: float64(dp.Count()), + sum: dp.Sum(), + } + + // bounds specifies the boundaries between buckets + // bucketCounts specifies the number of datapoints in each bucket + // there is always 1 more bucket count than there is boundaries + // len(bucketCounts) = len(bounds) + 1 + bounds := dp.ExplicitBounds() + lenBounds := bounds.Len() + bucketCounts := dp.BucketCounts() + lenBucketCounts := bucketCounts.Len() + + // Special case: no boundaries implies a single bucket + if lenBounds == 0 { + em.counts = append(em.counts, float64(bucketCounts.At(0))) // recall that len(bucketCounts) = len(bounds)+1 + if dp.HasMax() && dp.HasMin() { + em.values = append(em.values, em.minimum/2.0+em.maximum/2.0) // overflow safe average calculation + } else if dp.HasMax() { + em.values = append(em.values, em.maximum) // only data point we have is the maximum + } else if dp.HasMin() { + em.values = append(em.values, em.minimum) // only data point we have is the minimum + } else { + em.values = append(em.values, 0) // arbitrary value + } + return em + } + + // To create inner buckets, all outer buckets need to have defined boundaries. The first and last bucket use the + // min and max and their lower and upper bounds respectively. The min and max are optional on the OTel datapoint. + // When min and max are not defined, make some reasonable about about what the min/max could be + if !dp.HasMin() { + bucketWidth := 0.001 // arbitrary width - there's no information about this histogram to make an inference with + if lenBounds > 1 { + bucketWidth = bounds.At(1) - bounds.At(0) + } + em.minimum = bounds.At(0) - bucketWidth + } + + if !dp.HasMax() { + bucketWidth := 0.01 // arbitrary width - there's no information about this histogram to make an inference with + if lenBounds > 1 { + bucketWidth = bounds.At(lenBounds-2) - bounds.At(lenBounds-1) + } + em.maximum = bounds.At(lenBounds-1) + bucketWidth + } + + // Pre-calculate total output size to avoid dynamic growth + totalOutputSize := 0 + for i := 0; i < lenBucketCounts; i++ { + sampleCount := bucketCounts.At(i) + if sampleCount > 0 { + totalOutputSize += int(min(sampleCount, maximumInnerBucketCount)) + } + } + if totalOutputSize == 0 { + // No samples in any bucket + return em + } + + em.values = make([]float64, 0, totalOutputSize) + em.counts = make([]float64, 0, totalOutputSize) + + for i := 0; i < lenBucketCounts; i++ { + sampleCount := int(bucketCounts.At(i)) + if sampleCount == 0 { + // No need to operate on a bucket with no samples + continue + } + + lowerBound := em.minimum + if i > 0 { + lowerBound = bounds.At(i - 1) + } + upperBound := em.maximum + if i < lenBucketCounts-1 { + upperBound = bounds.At(i) + } + + // This algorithm creates "inner buckets" between user-defined bucket based on the sample count, up to a + // maximum. A logarithmic ratio (named "magnitude") compares the density between the current bucket and the + // next bucket. This logarithmic ratio is used to decide how to spread samples amongst inner buckets. + // + // case 1: magnitude < 0 + // * What this means: Current bucket is denser than the next bucket -> density is decreasing. + // * What we do: Use inverse quadratic distribution to spread the samples. This allocates more samples towards + // the lower bound of the bucket. + // case 2: 0 <= magnitude < 1 + // * What this means: Current bucket and next bucket has similar densities -> density is not changing much. + // * What we do: Use inform distribution to spread the samples. Extra samples that can't be spread evenly are + // (arbitrarily) allocated towards the start of the bucket. + // case 3: 1 <= magnitude + // * What this means: Current bucket is less dense than the next bucket -> density is increasing. + // * What we do: Use quadratic distribution to spread the samples. This allocates more samples toward the end + // of the bucket. + // + // As a small optimization, we omit the logarithm invocation and change the thresholds. + ratio := 0.0 + if i < lenBucketCounts-1 { + nextSampleCount := bucketCounts.At(i + 1) + // If next bucket is empty, than density is surely decreasing + if nextSampleCount == 0 { + ratio = 0.0 + } else { + var nextUpperBound float64 + if i+1 == lenBucketCounts-1 { + nextUpperBound = em.maximum + } else { + nextUpperBound = bounds.At(i + 1) + } + + //currentBucketDensity := float64(sampleCount) / (upperBound - lowerBound) + //nextBucketDensity := float64(nextSampleCount) / (nextUpperBound - upperBound) + //ratio = nextBucketDensity / currentBucketDensity + + // the following calculations are the same but improves speed by ~1% in benchmark tests + denom := (nextUpperBound - upperBound) * float64(sampleCount) + numerator := (upperBound - lowerBound) * float64(nextSampleCount) + ratio = numerator / denom + } + } + + // innerBucketCount is how many "inner buckets" to spread the sample count amongst + innerBucketCount := min(sampleCount, maximumInnerBucketCount) + delta := (upperBound - lowerBound) / float64(innerBucketCount) + + if ratio < 1.0/math.E { // magnitude < 0: Use -yx^2 (inverse quadratic) + sigma := float64(sumOfSquares(innerBucketCount)) + epsilon := float64(sampleCount) / sigma + entryStart := len(em.counts) + + runningSum := 0 + for j := 0; j < innerBucketCount; j++ { + innerBucketSampleCount := epsilon * float64((j-innerBucketCount)*(j-innerBucketCount)) + innerBucketSampleCountAdjusted := int(math.Floor(innerBucketSampleCount)) + if innerBucketSampleCountAdjusted > 0 { + runningSum += innerBucketSampleCountAdjusted + em.values = append(em.values, lowerBound+delta*float64(j+1)) + em.counts = append(em.counts, float64(innerBucketSampleCountAdjusted)) + } + } + + // distribute the remainder towards the front + remainder := sampleCount - runningSum + // make sure there's room for the remainder + if len(em.counts) < entryStart+remainder { + em.counts = append(em.counts, make([]float64, remainder)...) + em.values = append(em.values, make([]float64, remainder)...) + } + for j := 0; j < remainder; j++ { + em.counts[entryStart] += 1 + entryStart += 1 + } + + } else if ratio < math.E { // 0 <= magnitude < 1: Use x + // Distribute samples evenly with integer counts + baseCount := sampleCount / innerBucketCount + remainder := sampleCount % innerBucketCount + for j := 1; j <= innerBucketCount; j++ { + count := baseCount + + // Distribute remainder to first few buckets + if j <= remainder { + count++ + } + em.values = append(em.values, lowerBound+delta*float64(j)) + em.counts = append(em.counts, float64(count)) + } + + } else { // magnitude >= 1: Use yx^2 (quadratic) + sigma := float64(sumOfSquares(innerBucketCount)) + epsilon := float64(sampleCount) / sigma + entryStart := len(em.counts) + + runningSum := 0 + for j := 1; j <= innerBucketCount; j++ { + innerBucketSampleCount := epsilon * float64(j*j) + innerBucketSampleCountAdjusted := int(math.Floor(innerBucketSampleCount)) + if innerBucketSampleCountAdjusted > 0 { + runningSum += innerBucketSampleCountAdjusted + em.values = append(em.values, lowerBound+delta*float64(j)) + em.counts = append(em.counts, float64(innerBucketSampleCountAdjusted)) + } + } + + // distribute the remainder towards the end + remainder := sampleCount - runningSum + // make sure there's room for the remainder + if len(em.counts) < entryStart+remainder { + em.counts = append(em.counts, make([]float64, remainder)...) + em.values = append(em.values, make([]float64, remainder)...) + } + entryStart = len(em.counts) - 1 + for j := 0; j < remainder; j++ { + em.counts[entryStart] += 1 + entryStart -= 1 + } + } + + } + + // Move last entry to maximum if needed + if dp.HasMax() && len(em.values) > 0 { + lastIdx := len(em.values) - 1 + for i := lastIdx; i >= 0; i-- { + if em.counts[i] > 0 { + lastIdx = i + break + } + } + em.values[lastIdx] = em.maximum + em.values = em.values[:lastIdx+1] + em.counts = em.counts[:lastIdx+1] + } + + return em +} + +func (em *ExponentialMapping) ValuesAndCounts() ([]float64, []float64) { + return em.values, em.counts +} + +func (em *ExponentialMapping) Minimum() float64 { + return em.minimum +} + +func (em *ExponentialMapping) Maximum() float64 { + return em.maximum +} + +func (em *ExponentialMapping) SampleCount() float64 { + return em.sampleCount +} + +func (em *ExponentialMapping) Sum() float64 { + return em.sum +} + +// sumOfSquares is a closed form calculation of Σx^2, for 1 to n +func sumOfSquares(n int) int { + return n * (n + 1) * (2*n + 1) / 6 +} diff --git a/pkg/aws/cloudwatch/histograms/conversion_test.go b/pkg/aws/cloudwatch/histograms/conversion_test.go new file mode 100644 index 0000000000000..fd9a9e865efa3 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/conversion_test.go @@ -0,0 +1,290 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package histograms // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/aws/cloudwatch/histograms" + +import ( + "encoding/csv" + "encoding/json" + "fmt" + "math" + "os" + "sort" + "strconv" + "strings" + "testing" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/aws/cloudwatch" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pmetric" +) + +func TestConvertOTelToCloudWatch(t *testing.T) { + + for _, tc := range TestCases() { + t.Run(tc.Name, func(t *testing.T) { + dp := setupDatapoint(tc.Input) + dist := ConvertOTelToCloudWatch(dp) + verifyDist(t, dist, tc.Expected) + + // uncomment next lines to write datapoint to JSON file for visual inspection + // use histogram_mappings.py to create graphs + //var filenameReplacer = strings.NewReplacer( + // " ", "_", + // "/", "_", + //) + // assert.NoError(t, writeValuesAndCountsToJson(dist, filenameReplacer.Replace(tc.Name+".json"))) + }) + } + + t.Run("accuracy test - lognormal", func(t *testing.T) { + verifyDistAccuracy(t, ConvertOTelToCloudWatch, "testdata/lognormal_10000.csv") + }) + + t.Run("accuracy test - weibull", func(t *testing.T) { + verifyDistAccuracy(t, ConvertOTelToCloudWatch, "testdata/weibull_10000.csv") + }) + +} + +func BenchmarkLogNormal(b *testing.B) { + // arrange + boundaries := []float64{ + 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01, + 0.011, 0.012, 0.013, 0.014, 0.015, 0.016, 0.017, 0.018, 0.019, 0.02, + 0.021, 0.022, 0.023, 0.024, 0.025, 0.026, 0.027, 0.028, 0.029, 0.03, + 0.031, 0.032, 0.033, 0.034, 0.035, 0.036, 0.037, 0.038, 0.039, 0.04, + 0.041, 0.042, 0.043, 0.044, 0.045, 0.046, 0.047, 0.048, 0.049, 0.05, + 0.1, 0.2, + } + + data, err := loadCsvData("testdata/lognormal_10000.csv") + require.NoError(b, err) + require.Len(b, data, 10000) + + dp := createHistogramDatapointFromData(data, boundaries) + require.Equal(b, int(dp.Count()), 10000) + + b.Run("NewExponentialMappingCWFromOtel", func(b *testing.B) { + for i := 0; i < b.N; i++ { + dist := ConvertOTelToCloudWatch(dp) + values, counts := dist.ValuesAndCounts() + assert.NotNil(b, values) + assert.NotNil(b, counts) + } + }) + +} + +func BenchmarkWeibull(b *testing.B) { + // arrange + boundaries := []float64{ + 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01, + 0.011, 0.012, 0.013, 0.014, 0.015, 0.016, 0.017, 0.018, 0.019, 0.02, + 0.021, 0.022, 0.023, 0.024, 0.025, 0.026, 0.027, 0.028, 0.029, 0.03, + 0.031, 0.032, 0.033, 0.034, 0.035, 0.036, 0.037, 0.038, 0.039, 0.04, + 0.041, 0.042, 0.043, 0.044, 0.045, 0.046, 0.047, 0.048, 0.049, 0.05, + 0.1, 0.2, + } + + data, err := loadCsvData("testdata/weibull_10000.csv") + require.NoError(b, err) + require.Len(b, data, 10000) + + dp := createHistogramDatapointFromData(data, boundaries) + require.Equal(b, int(dp.Count()), 10000) + + b.Run("NewExponentialMappingCWFromOtel", func(b *testing.B) { + for i := 0; i < b.N; i++ { + dist := ConvertOTelToCloudWatch(dp) + values, counts := dist.ValuesAndCounts() + assert.NotNil(b, values) + assert.NotNil(b, counts) + } + }) + +} + +func setupDatapoint(input HistogramInput) pmetric.HistogramDataPoint { + dp := pmetric.NewHistogramDataPoint() + dp.SetCount(input.Count) + dp.SetSum(input.Sum) + if input.Min != nil { + dp.SetMin(*input.Min) + } + if input.Max != nil { + dp.SetMax(*input.Max) + } + dp.ExplicitBounds().FromRaw(input.Boundaries) + dp.BucketCounts().FromRaw(input.Counts) + return dp +} + +func verifyDist(t *testing.T, dist cloudwatch.HistogramDataPoint, expected ExpectedMetrics) { + + if expected.Min != nil { + assert.Equal(t, *expected.Min, dist.Minimum(), "min does not match expected") + } + if expected.Max != nil { + assert.Equal(t, *expected.Max, dist.Maximum(), "max does not match expected") + } + assert.Equal(t, int(expected.Count), int(dist.SampleCount()), "samplecount does not match expected") + assert.Equal(t, expected.Sum, dist.Sum(), "sum does not match expected") + + values, counts := dist.ValuesAndCounts() + + calculatedCount := 0.0 + for _, count := range counts { + calculatedCount += count + //fmt.Printf("%7.2f = %4d (%d)\n", values[i], int(counts[i]), calculatedCount) + } + assert.InDelta(t, float64(expected.Count), calculatedCount, 1e-6, "calculated count does not match expected") + + for p, r := range expected.PercentileRanges { + x := int(math.Round(float64(dist.SampleCount()) * p)) + + soFar := 0 + for i, count := range counts { + soFar += int(count) + if soFar >= x { + //fmt.Printf("Found p%.f at bucket %0.2f. Expected range: %+v\n", p*100, values[i], r) + assert.GreaterOrEqual(t, values[i], r.Low, "percentile %0.2f", p) + assert.LessOrEqual(t, values[i], r.High, "percentile %0.2f", p) + break + } + } + } +} + +func loadCsvData(filename string) ([]float64, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + + reader := csv.NewReader(file) + records, err := reader.ReadAll() + if err != nil { + return nil, err + } + + var data []float64 + for _, value := range records[0] { + f, err := strconv.ParseFloat(strings.TrimSpace(value), 64) + if err != nil { + return nil, err + } + data = append(data, f) + } + return data, nil +} + +func createHistogramDatapointFromData(data []float64, boundaries []float64) pmetric.HistogramDataPoint { + dp := pmetric.NewHistogramDataPoint() + + // Calculate basic stats + var sum float64 + min := math.Inf(1) + max := math.Inf(-1) + + for _, v := range data { + sum += v + if v < min { + min = v + } + if v > max { + max = v + } + } + + dp.SetCount(uint64(len(data))) + dp.SetSum(sum) + dp.SetMin(min) + dp.SetMax(max) + + // Create bucket counts + bucketCounts := make([]uint64, len(boundaries)+1) + + for _, v := range data { + bucket := sort.SearchFloat64s(boundaries, v) + bucketCounts[bucket]++ + } + + dp.ExplicitBounds().FromRaw(boundaries) + dp.BucketCounts().FromRaw(bucketCounts) + + return dp +} + +func verifyDistAccuracy(t *testing.T, newDistFunc func(pmetric.HistogramDataPoint) cloudwatch.HistogramDataPoint, filename string) { + // arrange + percentiles := []float64{0.1, 0.25, 0.5, 0.75, 0.9, 0.99, 0.999} + boundaries := []float64{ + 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01, + 0.011, 0.012, 0.013, 0.014, 0.015, 0.016, 0.017, 0.018, 0.019, 0.02, + 0.021, 0.022, 0.023, 0.024, 0.025, 0.026, 0.027, 0.028, 0.029, 0.03, + 0.031, 0.032, 0.033, 0.034, 0.035, 0.036, 0.037, 0.038, 0.039, 0.04, + 0.041, 0.042, 0.043, 0.044, 0.045, 0.046, 0.047, 0.048, 0.049, 0.05, + 0.1, 0.2, + } + + data, err := loadCsvData(filename) + require.NoError(t, err) + assert.Len(t, data, 10000) + + dp := createHistogramDatapointFromData(data, boundaries) + assert.Equal(t, int(dp.Count()), 10000) + calculatedTotal := 0 + for _, count := range dp.BucketCounts().All() { + calculatedTotal += int(count) + } + assert.Equal(t, calculatedTotal, 10000) + + // act + dist := newDistFunc(dp) + values, counts := dist.ValuesAndCounts() + + // assert + calculatedCount := 0.0 + for _, count := range counts { + calculatedCount += count + } + assert.InDelta(t, 10000, calculatedCount, 1e-6, "calculated count does not match expected") + + for _, p := range percentiles { + x1 := int(math.Round(float64(dp.Count()) * p)) + x2 := int(math.Round(calculatedCount * p)) + + exactPercentileValue := data[x1] + + soFar := 0 + for i, count := range counts { + soFar += int(count) + if soFar >= x2 { + calculatedPercentileValue := values[i] + errorPercent := (exactPercentileValue - calculatedPercentileValue) / exactPercentileValue * 100 + fmt.Printf("P%.1f: exact=%.6f, calculated=%.6f, error=%.2f%%\n", p*100, exactPercentileValue, calculatedPercentileValue, errorPercent) + break + } + + } + + } +} + +func writeValuesAndCountsToJson(dist cloudwatch.HistogramDataPoint, filename string) error { + values, counts := dist.ValuesAndCounts() + + data := make(map[string][]float64) + data["values"] = values + data["counts"] = counts + + jsonData, err := json.MarshalIndent(data, "", " ") + if err != nil { + return err + } + + return os.WriteFile(filename, jsonData, 0644) +} diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/126_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/126_Buckets.json new file mode 100644 index 0000000000000..be9ec93916a74 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/126_Buckets.json @@ -0,0 +1,2506 @@ +{ + "counts": [ + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 2, + 2, + 1, + 3, + 3, + 2, + 2, + 1 + ], + "values": [ + 5.5, + 6, + 6.5, + 7, + 7.5, + 8, + 8.5, + 9, + 9.5, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 475, + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493, + 494, + 495, + 496, + 497, + 498, + 499, + 500, + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 534, + 535, + 536, + 537, + 538, + 539, + 540, + 541, + 542, + 543, + 544, + 545, + 546, + 547, + 548, + 549, + 550, + 551, + 552, + 553, + 554, + 555, + 556, + 557, + 558, + 559, + 560, + 561, + 562, + 563, + 564, + 565, + 566, + 567, + 568, + 569, + 570, + 571, + 572, + 573, + 574, + 575, + 576, + 577, + 578, + 579, + 580, + 581, + 582, + 583, + 584, + 585, + 586, + 587, + 588, + 589, + 590, + 591, + 592, + 593, + 594, + 595, + 596, + 597, + 598, + 599, + 600, + 601, + 602, + 603, + 604, + 605, + 606, + 607, + 608, + 609, + 610, + 611, + 612, + 613, + 614, + 615, + 616, + 617, + 618, + 619, + 620, + 621, + 622, + 623, + 624, + 625, + 626, + 627, + 628, + 629, + 630, + 631, + 632, + 633, + 634, + 635, + 636, + 637, + 638, + 639, + 640, + 641, + 642, + 643, + 644, + 645, + 646, + 647, + 648, + 649, + 650, + 651, + 652, + 653, + 654, + 655, + 656, + 657, + 658, + 659, + 660, + 661, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 671, + 672, + 673, + 674, + 675, + 676, + 677, + 678, + 679, + 680, + 681, + 682, + 683, + 684, + 685, + 686, + 687, + 688, + 689, + 690, + 691, + 692, + 693, + 694, + 695, + 696, + 697, + 698, + 699, + 700, + 701, + 702, + 703, + 704, + 705, + 706, + 707, + 708, + 709, + 710, + 711, + 712, + 713, + 714, + 715, + 716, + 717, + 718, + 719, + 720, + 721, + 722, + 723, + 724, + 725, + 726, + 727, + 728, + 729, + 730, + 731, + 732, + 733, + 734, + 735, + 736, + 737, + 738, + 739, + 740, + 741, + 742, + 743, + 744, + 745, + 746, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 755, + 756, + 757, + 758, + 759, + 760, + 761, + 762, + 763, + 764, + 765, + 766, + 767, + 768, + 769, + 770, + 771, + 772, + 773, + 774, + 775, + 776, + 777, + 778, + 779, + 780, + 781, + 782, + 783, + 784, + 785, + 786, + 787, + 788, + 789, + 790, + 791, + 792, + 793, + 794, + 795, + 796, + 797, + 798, + 799, + 800, + 801, + 802, + 803, + 804, + 805, + 806, + 807, + 808, + 809, + 810, + 811, + 812, + 813, + 814, + 815, + 816, + 817, + 818, + 819, + 820, + 821, + 822, + 823, + 824, + 825, + 826, + 827, + 828, + 829, + 830, + 831, + 832, + 833, + 834, + 835, + 836, + 837, + 838, + 839, + 840, + 841, + 842, + 843, + 844, + 845, + 846, + 847, + 848, + 849, + 850, + 851, + 852, + 853, + 854, + 855, + 856, + 857, + 858, + 859, + 860, + 861, + 862, + 863, + 864, + 865, + 866, + 867, + 868, + 869, + 870, + 871, + 872, + 873, + 874, + 875, + 876, + 877, + 878, + 879, + 880, + 881, + 882, + 883, + 884, + 885, + 886, + 887, + 888, + 889, + 890, + 891, + 892, + 893, + 894, + 895, + 896, + 897, + 898, + 899, + 900, + 901, + 902, + 903, + 904, + 905, + 906, + 907, + 908, + 909, + 910, + 911, + 912, + 913, + 914, + 915, + 916, + 917, + 918, + 919, + 920, + 921, + 922, + 923, + 924, + 925, + 926, + 927, + 928, + 929, + 930, + 931, + 932, + 933, + 934, + 935, + 936, + 937, + 938, + 939, + 940, + 941, + 942, + 943, + 944, + 945, + 946, + 947, + 948, + 949, + 950, + 951, + 952, + 953, + 954, + 955, + 956, + 957, + 958, + 959, + 960, + 961, + 962, + 963, + 964, + 965, + 966, + 967, + 968, + 969, + 970, + 971, + 972, + 973, + 974, + 975, + 976, + 977, + 978, + 979, + 980, + 981, + 982, + 983, + 984, + 985, + 986, + 987, + 988, + 989, + 990, + 991, + 992, + 993, + 994, + 995, + 996, + 997, + 998, + 999, + 1000, + 1001, + 1002, + 1003, + 1004, + 1005, + 1006, + 1007, + 1008, + 1009, + 1010, + 1011, + 1012, + 1013, + 1014, + 1015, + 1016, + 1017, + 1018, + 1019, + 1020, + 1021, + 1022, + 1023, + 1024, + 1025, + 1026, + 1027, + 1028, + 1029, + 1030, + 1031, + 1032, + 1033, + 1034, + 1035, + 1036, + 1037, + 1038, + 1039, + 1040, + 1041, + 1042, + 1043, + 1044, + 1045, + 1046, + 1047, + 1048, + 1049, + 1050, + 1051, + 1052, + 1053, + 1054, + 1055, + 1056, + 1057, + 1058, + 1059, + 1060, + 1061, + 1062, + 1063, + 1064, + 1065, + 1066, + 1067, + 1068, + 1069, + 1070, + 1071, + 1072, + 1073, + 1074, + 1075, + 1076, + 1077, + 1078, + 1079, + 1080, + 1081, + 1082, + 1083, + 1084, + 1085, + 1086, + 1087, + 1088, + 1089, + 1090, + 1091, + 1092, + 1093, + 1094, + 1095, + 1096, + 1097, + 1098, + 1099, + 1100, + 1101, + 1102, + 1103, + 1104, + 1105, + 1106, + 1107, + 1108, + 1109, + 1110, + 1111, + 1112, + 1113, + 1114, + 1115, + 1116, + 1117, + 1118, + 1119, + 1120, + 1121, + 1122, + 1123, + 1124, + 1125, + 1126, + 1127, + 1128, + 1129, + 1130, + 1131, + 1132, + 1133, + 1134, + 1135, + 1136, + 1137, + 1138, + 1139, + 1140, + 1141, + 1142, + 1143, + 1144, + 1145, + 1146, + 1147, + 1148, + 1149, + 1150, + 1151, + 1152, + 1153, + 1154, + 1155, + 1156, + 1157, + 1158, + 1159, + 1160, + 1161, + 1162, + 1163, + 1164, + 1165, + 1166, + 1167, + 1168, + 1169, + 1170, + 1171, + 1172, + 1173, + 1174, + 1175, + 1176, + 1177, + 1178, + 1179, + 1180, + 1181, + 1182, + 1183, + 1184, + 1185, + 1186, + 1187, + 1188, + 1189, + 1190, + 1191, + 1192, + 1193, + 1194, + 1195, + 1196, + 1197, + 1198, + 1199, + 1200, + 1201, + 1202, + 1203, + 1204, + 1205, + 1206, + 1207, + 1208, + 1209, + 1210, + 1211, + 1212, + 1213, + 1214, + 1215, + 1216, + 1217, + 1218, + 1219, + 1220, + 1221, + 1222, + 1223, + 1224, + 1225, + 1226, + 1227, + 1228, + 1229, + 1230, + 1231, + 1232, + 1233, + 1234, + 1235, + 1236, + 1237, + 1238, + 1239, + 1240, + 1241, + 1242, + 1243, + 1244, + 1245, + 1255, + 1260, + 1265, + 1270, + 1300 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/176_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/176_Buckets.json new file mode 100644 index 0000000000000..7fefaa6d88591 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/176_Buckets.json @@ -0,0 +1,3506 @@ +{ + "counts": [ + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 2, + 2, + 1, + 3, + 3, + 2, + 2, + 1 + ], + "values": [ + 5.5, + 6, + 6.5, + 7, + 7.5, + 8, + 8.5, + 9, + 9.5, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 475, + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493, + 494, + 495, + 496, + 497, + 498, + 499, + 500, + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 534, + 535, + 536, + 537, + 538, + 539, + 540, + 541, + 542, + 543, + 544, + 545, + 546, + 547, + 548, + 549, + 550, + 551, + 552, + 553, + 554, + 555, + 556, + 557, + 558, + 559, + 560, + 561, + 562, + 563, + 564, + 565, + 566, + 567, + 568, + 569, + 570, + 571, + 572, + 573, + 574, + 575, + 576, + 577, + 578, + 579, + 580, + 581, + 582, + 583, + 584, + 585, + 586, + 587, + 588, + 589, + 590, + 591, + 592, + 593, + 594, + 595, + 596, + 597, + 598, + 599, + 600, + 601, + 602, + 603, + 604, + 605, + 606, + 607, + 608, + 609, + 610, + 611, + 612, + 613, + 614, + 615, + 616, + 617, + 618, + 619, + 620, + 621, + 622, + 623, + 624, + 625, + 626, + 627, + 628, + 629, + 630, + 631, + 632, + 633, + 634, + 635, + 636, + 637, + 638, + 639, + 640, + 641, + 642, + 643, + 644, + 645, + 646, + 647, + 648, + 649, + 650, + 651, + 652, + 653, + 654, + 655, + 656, + 657, + 658, + 659, + 660, + 661, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 671, + 672, + 673, + 674, + 675, + 676, + 677, + 678, + 679, + 680, + 681, + 682, + 683, + 684, + 685, + 686, + 687, + 688, + 689, + 690, + 691, + 692, + 693, + 694, + 695, + 696, + 697, + 698, + 699, + 700, + 701, + 702, + 703, + 704, + 705, + 706, + 707, + 708, + 709, + 710, + 711, + 712, + 713, + 714, + 715, + 716, + 717, + 718, + 719, + 720, + 721, + 722, + 723, + 724, + 725, + 726, + 727, + 728, + 729, + 730, + 731, + 732, + 733, + 734, + 735, + 736, + 737, + 738, + 739, + 740, + 741, + 742, + 743, + 744, + 745, + 746, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 755, + 756, + 757, + 758, + 759, + 760, + 761, + 762, + 763, + 764, + 765, + 766, + 767, + 768, + 769, + 770, + 771, + 772, + 773, + 774, + 775, + 776, + 777, + 778, + 779, + 780, + 781, + 782, + 783, + 784, + 785, + 786, + 787, + 788, + 789, + 790, + 791, + 792, + 793, + 794, + 795, + 796, + 797, + 798, + 799, + 800, + 801, + 802, + 803, + 804, + 805, + 806, + 807, + 808, + 809, + 810, + 811, + 812, + 813, + 814, + 815, + 816, + 817, + 818, + 819, + 820, + 821, + 822, + 823, + 824, + 825, + 826, + 827, + 828, + 829, + 830, + 831, + 832, + 833, + 834, + 835, + 836, + 837, + 838, + 839, + 840, + 841, + 842, + 843, + 844, + 845, + 846, + 847, + 848, + 849, + 850, + 851, + 852, + 853, + 854, + 855, + 856, + 857, + 858, + 859, + 860, + 861, + 862, + 863, + 864, + 865, + 866, + 867, + 868, + 869, + 870, + 871, + 872, + 873, + 874, + 875, + 876, + 877, + 878, + 879, + 880, + 881, + 882, + 883, + 884, + 885, + 886, + 887, + 888, + 889, + 890, + 891, + 892, + 893, + 894, + 895, + 896, + 897, + 898, + 899, + 900, + 901, + 902, + 903, + 904, + 905, + 906, + 907, + 908, + 909, + 910, + 911, + 912, + 913, + 914, + 915, + 916, + 917, + 918, + 919, + 920, + 921, + 922, + 923, + 924, + 925, + 926, + 927, + 928, + 929, + 930, + 931, + 932, + 933, + 934, + 935, + 936, + 937, + 938, + 939, + 940, + 941, + 942, + 943, + 944, + 945, + 946, + 947, + 948, + 949, + 950, + 951, + 952, + 953, + 954, + 955, + 956, + 957, + 958, + 959, + 960, + 961, + 962, + 963, + 964, + 965, + 966, + 967, + 968, + 969, + 970, + 971, + 972, + 973, + 974, + 975, + 976, + 977, + 978, + 979, + 980, + 981, + 982, + 983, + 984, + 985, + 986, + 987, + 988, + 989, + 990, + 991, + 992, + 993, + 994, + 995, + 996, + 997, + 998, + 999, + 1000, + 1001, + 1002, + 1003, + 1004, + 1005, + 1006, + 1007, + 1008, + 1009, + 1010, + 1011, + 1012, + 1013, + 1014, + 1015, + 1016, + 1017, + 1018, + 1019, + 1020, + 1021, + 1022, + 1023, + 1024, + 1025, + 1026, + 1027, + 1028, + 1029, + 1030, + 1031, + 1032, + 1033, + 1034, + 1035, + 1036, + 1037, + 1038, + 1039, + 1040, + 1041, + 1042, + 1043, + 1044, + 1045, + 1046, + 1047, + 1048, + 1049, + 1050, + 1051, + 1052, + 1053, + 1054, + 1055, + 1056, + 1057, + 1058, + 1059, + 1060, + 1061, + 1062, + 1063, + 1064, + 1065, + 1066, + 1067, + 1068, + 1069, + 1070, + 1071, + 1072, + 1073, + 1074, + 1075, + 1076, + 1077, + 1078, + 1079, + 1080, + 1081, + 1082, + 1083, + 1084, + 1085, + 1086, + 1087, + 1088, + 1089, + 1090, + 1091, + 1092, + 1093, + 1094, + 1095, + 1096, + 1097, + 1098, + 1099, + 1100, + 1101, + 1102, + 1103, + 1104, + 1105, + 1106, + 1107, + 1108, + 1109, + 1110, + 1111, + 1112, + 1113, + 1114, + 1115, + 1116, + 1117, + 1118, + 1119, + 1120, + 1121, + 1122, + 1123, + 1124, + 1125, + 1126, + 1127, + 1128, + 1129, + 1130, + 1131, + 1132, + 1133, + 1134, + 1135, + 1136, + 1137, + 1138, + 1139, + 1140, + 1141, + 1142, + 1143, + 1144, + 1145, + 1146, + 1147, + 1148, + 1149, + 1150, + 1151, + 1152, + 1153, + 1154, + 1155, + 1156, + 1157, + 1158, + 1159, + 1160, + 1161, + 1162, + 1163, + 1164, + 1165, + 1166, + 1167, + 1168, + 1169, + 1170, + 1171, + 1172, + 1173, + 1174, + 1175, + 1176, + 1177, + 1178, + 1179, + 1180, + 1181, + 1182, + 1183, + 1184, + 1185, + 1186, + 1187, + 1188, + 1189, + 1190, + 1191, + 1192, + 1193, + 1194, + 1195, + 1196, + 1197, + 1198, + 1199, + 1200, + 1201, + 1202, + 1203, + 1204, + 1205, + 1206, + 1207, + 1208, + 1209, + 1210, + 1211, + 1212, + 1213, + 1214, + 1215, + 1216, + 1217, + 1218, + 1219, + 1220, + 1221, + 1222, + 1223, + 1224, + 1225, + 1226, + 1227, + 1228, + 1229, + 1230, + 1231, + 1232, + 1233, + 1234, + 1235, + 1236, + 1237, + 1238, + 1239, + 1240, + 1241, + 1242, + 1243, + 1244, + 1245, + 1246, + 1247, + 1248, + 1249, + 1250, + 1251, + 1252, + 1253, + 1254, + 1255, + 1256, + 1257, + 1258, + 1259, + 1260, + 1261, + 1262, + 1263, + 1264, + 1265, + 1266, + 1267, + 1268, + 1269, + 1270, + 1271, + 1272, + 1273, + 1274, + 1275, + 1276, + 1277, + 1278, + 1279, + 1280, + 1281, + 1282, + 1283, + 1284, + 1285, + 1286, + 1287, + 1288, + 1289, + 1290, + 1291, + 1292, + 1293, + 1294, + 1295, + 1296, + 1297, + 1298, + 1299, + 1300, + 1301, + 1302, + 1303, + 1304, + 1305, + 1306, + 1307, + 1308, + 1309, + 1310, + 1311, + 1312, + 1313, + 1314, + 1315, + 1316, + 1317, + 1318, + 1319, + 1320, + 1321, + 1322, + 1323, + 1324, + 1325, + 1326, + 1327, + 1328, + 1329, + 1330, + 1331, + 1332, + 1333, + 1334, + 1335, + 1336, + 1337, + 1338, + 1339, + 1340, + 1341, + 1342, + 1343, + 1344, + 1345, + 1346, + 1347, + 1348, + 1349, + 1350, + 1351, + 1352, + 1353, + 1354, + 1355, + 1356, + 1357, + 1358, + 1359, + 1360, + 1361, + 1362, + 1363, + 1364, + 1365, + 1366, + 1367, + 1368, + 1369, + 1370, + 1371, + 1372, + 1373, + 1374, + 1375, + 1376, + 1377, + 1378, + 1379, + 1380, + 1381, + 1382, + 1383, + 1384, + 1385, + 1386, + 1387, + 1388, + 1389, + 1390, + 1391, + 1392, + 1393, + 1394, + 1395, + 1396, + 1397, + 1398, + 1399, + 1400, + 1401, + 1402, + 1403, + 1404, + 1405, + 1406, + 1407, + 1408, + 1409, + 1410, + 1411, + 1412, + 1413, + 1414, + 1415, + 1416, + 1417, + 1418, + 1419, + 1420, + 1421, + 1422, + 1423, + 1424, + 1425, + 1426, + 1427, + 1428, + 1429, + 1430, + 1431, + 1432, + 1433, + 1434, + 1435, + 1436, + 1437, + 1438, + 1439, + 1440, + 1441, + 1442, + 1443, + 1444, + 1445, + 1446, + 1447, + 1448, + 1449, + 1450, + 1451, + 1452, + 1453, + 1454, + 1455, + 1456, + 1457, + 1458, + 1459, + 1460, + 1461, + 1462, + 1463, + 1464, + 1465, + 1466, + 1467, + 1468, + 1469, + 1470, + 1471, + 1472, + 1473, + 1474, + 1475, + 1476, + 1477, + 1478, + 1479, + 1480, + 1481, + 1482, + 1483, + 1484, + 1485, + 1486, + 1487, + 1488, + 1489, + 1490, + 1491, + 1492, + 1493, + 1494, + 1495, + 1496, + 1497, + 1498, + 1499, + 1500, + 1501, + 1502, + 1503, + 1504, + 1505, + 1506, + 1507, + 1508, + 1509, + 1510, + 1511, + 1512, + 1513, + 1514, + 1515, + 1516, + 1517, + 1518, + 1519, + 1520, + 1521, + 1522, + 1523, + 1524, + 1525, + 1526, + 1527, + 1528, + 1529, + 1530, + 1531, + 1532, + 1533, + 1534, + 1535, + 1536, + 1537, + 1538, + 1539, + 1540, + 1541, + 1542, + 1543, + 1544, + 1545, + 1546, + 1547, + 1548, + 1549, + 1550, + 1551, + 1552, + 1553, + 1554, + 1555, + 1556, + 1557, + 1558, + 1559, + 1560, + 1561, + 1562, + 1563, + 1564, + 1565, + 1566, + 1567, + 1568, + 1569, + 1570, + 1571, + 1572, + 1573, + 1574, + 1575, + 1576, + 1577, + 1578, + 1579, + 1580, + 1581, + 1582, + 1583, + 1584, + 1585, + 1586, + 1587, + 1588, + 1589, + 1590, + 1591, + 1592, + 1593, + 1594, + 1595, + 1596, + 1597, + 1598, + 1599, + 1600, + 1601, + 1602, + 1603, + 1604, + 1605, + 1606, + 1607, + 1608, + 1609, + 1610, + 1611, + 1612, + 1613, + 1614, + 1615, + 1616, + 1617, + 1618, + 1619, + 1620, + 1621, + 1622, + 1623, + 1624, + 1625, + 1626, + 1627, + 1628, + 1629, + 1630, + 1631, + 1632, + 1633, + 1634, + 1635, + 1636, + 1637, + 1638, + 1639, + 1640, + 1641, + 1642, + 1643, + 1644, + 1645, + 1646, + 1647, + 1648, + 1649, + 1650, + 1651, + 1652, + 1653, + 1654, + 1655, + 1656, + 1657, + 1658, + 1659, + 1660, + 1661, + 1662, + 1663, + 1664, + 1665, + 1666, + 1667, + 1668, + 1669, + 1670, + 1671, + 1672, + 1673, + 1674, + 1675, + 1676, + 1677, + 1678, + 1679, + 1680, + 1681, + 1682, + 1683, + 1684, + 1685, + 1686, + 1687, + 1688, + 1689, + 1690, + 1691, + 1692, + 1693, + 1694, + 1695, + 1696, + 1697, + 1698, + 1699, + 1700, + 1701, + 1702, + 1703, + 1704, + 1705, + 1706, + 1707, + 1708, + 1709, + 1710, + 1711, + 1712, + 1713, + 1714, + 1715, + 1716, + 1717, + 1718, + 1719, + 1720, + 1721, + 1722, + 1723, + 1724, + 1725, + 1726, + 1727, + 1728, + 1729, + 1730, + 1731, + 1732, + 1733, + 1734, + 1735, + 1736, + 1737, + 1738, + 1739, + 1740, + 1741, + 1742, + 1743, + 1744, + 1745, + 1755, + 1760, + 1765, + 1770, + 1800 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/225_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/225_Buckets.json new file mode 100644 index 0000000000000..787c23e12554c --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/225_Buckets.json @@ -0,0 +1,4506 @@ +{ + "counts": [ + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 2, + 2, + 1, + 3, + 3, + 2, + 2, + 1 + ], + "values": [ + 5.5, + 6, + 6.5, + 7, + 7.5, + 8, + 8.5, + 9, + 9.5, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 475, + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493, + 494, + 495, + 496, + 497, + 498, + 499, + 500, + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 534, + 535, + 536, + 537, + 538, + 539, + 540, + 541, + 542, + 543, + 544, + 545, + 546, + 547, + 548, + 549, + 550, + 551, + 552, + 553, + 554, + 555, + 556, + 557, + 558, + 559, + 560, + 561, + 562, + 563, + 564, + 565, + 566, + 567, + 568, + 569, + 570, + 571, + 572, + 573, + 574, + 575, + 576, + 577, + 578, + 579, + 580, + 581, + 582, + 583, + 584, + 585, + 586, + 587, + 588, + 589, + 590, + 591, + 592, + 593, + 594, + 595, + 596, + 597, + 598, + 599, + 600, + 601, + 602, + 603, + 604, + 605, + 606, + 607, + 608, + 609, + 610, + 611, + 612, + 613, + 614, + 615, + 616, + 617, + 618, + 619, + 620, + 621, + 622, + 623, + 624, + 625, + 626, + 627, + 628, + 629, + 630, + 631, + 632, + 633, + 634, + 635, + 636, + 637, + 638, + 639, + 640, + 641, + 642, + 643, + 644, + 645, + 646, + 647, + 648, + 649, + 650, + 651, + 652, + 653, + 654, + 655, + 656, + 657, + 658, + 659, + 660, + 661, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 671, + 672, + 673, + 674, + 675, + 676, + 677, + 678, + 679, + 680, + 681, + 682, + 683, + 684, + 685, + 686, + 687, + 688, + 689, + 690, + 691, + 692, + 693, + 694, + 695, + 696, + 697, + 698, + 699, + 700, + 701, + 702, + 703, + 704, + 705, + 706, + 707, + 708, + 709, + 710, + 711, + 712, + 713, + 714, + 715, + 716, + 717, + 718, + 719, + 720, + 721, + 722, + 723, + 724, + 725, + 726, + 727, + 728, + 729, + 730, + 731, + 732, + 733, + 734, + 735, + 736, + 737, + 738, + 739, + 740, + 741, + 742, + 743, + 744, + 745, + 746, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 755, + 756, + 757, + 758, + 759, + 760, + 761, + 762, + 763, + 764, + 765, + 766, + 767, + 768, + 769, + 770, + 771, + 772, + 773, + 774, + 775, + 776, + 777, + 778, + 779, + 780, + 781, + 782, + 783, + 784, + 785, + 786, + 787, + 788, + 789, + 790, + 791, + 792, + 793, + 794, + 795, + 796, + 797, + 798, + 799, + 800, + 801, + 802, + 803, + 804, + 805, + 806, + 807, + 808, + 809, + 810, + 811, + 812, + 813, + 814, + 815, + 816, + 817, + 818, + 819, + 820, + 821, + 822, + 823, + 824, + 825, + 826, + 827, + 828, + 829, + 830, + 831, + 832, + 833, + 834, + 835, + 836, + 837, + 838, + 839, + 840, + 841, + 842, + 843, + 844, + 845, + 846, + 847, + 848, + 849, + 850, + 851, + 852, + 853, + 854, + 855, + 856, + 857, + 858, + 859, + 860, + 861, + 862, + 863, + 864, + 865, + 866, + 867, + 868, + 869, + 870, + 871, + 872, + 873, + 874, + 875, + 876, + 877, + 878, + 879, + 880, + 881, + 882, + 883, + 884, + 885, + 886, + 887, + 888, + 889, + 890, + 891, + 892, + 893, + 894, + 895, + 896, + 897, + 898, + 899, + 900, + 901, + 902, + 903, + 904, + 905, + 906, + 907, + 908, + 909, + 910, + 911, + 912, + 913, + 914, + 915, + 916, + 917, + 918, + 919, + 920, + 921, + 922, + 923, + 924, + 925, + 926, + 927, + 928, + 929, + 930, + 931, + 932, + 933, + 934, + 935, + 936, + 937, + 938, + 939, + 940, + 941, + 942, + 943, + 944, + 945, + 946, + 947, + 948, + 949, + 950, + 951, + 952, + 953, + 954, + 955, + 956, + 957, + 958, + 959, + 960, + 961, + 962, + 963, + 964, + 965, + 966, + 967, + 968, + 969, + 970, + 971, + 972, + 973, + 974, + 975, + 976, + 977, + 978, + 979, + 980, + 981, + 982, + 983, + 984, + 985, + 986, + 987, + 988, + 989, + 990, + 991, + 992, + 993, + 994, + 995, + 996, + 997, + 998, + 999, + 1000, + 1001, + 1002, + 1003, + 1004, + 1005, + 1006, + 1007, + 1008, + 1009, + 1010, + 1011, + 1012, + 1013, + 1014, + 1015, + 1016, + 1017, + 1018, + 1019, + 1020, + 1021, + 1022, + 1023, + 1024, + 1025, + 1026, + 1027, + 1028, + 1029, + 1030, + 1031, + 1032, + 1033, + 1034, + 1035, + 1036, + 1037, + 1038, + 1039, + 1040, + 1041, + 1042, + 1043, + 1044, + 1045, + 1046, + 1047, + 1048, + 1049, + 1050, + 1051, + 1052, + 1053, + 1054, + 1055, + 1056, + 1057, + 1058, + 1059, + 1060, + 1061, + 1062, + 1063, + 1064, + 1065, + 1066, + 1067, + 1068, + 1069, + 1070, + 1071, + 1072, + 1073, + 1074, + 1075, + 1076, + 1077, + 1078, + 1079, + 1080, + 1081, + 1082, + 1083, + 1084, + 1085, + 1086, + 1087, + 1088, + 1089, + 1090, + 1091, + 1092, + 1093, + 1094, + 1095, + 1096, + 1097, + 1098, + 1099, + 1100, + 1101, + 1102, + 1103, + 1104, + 1105, + 1106, + 1107, + 1108, + 1109, + 1110, + 1111, + 1112, + 1113, + 1114, + 1115, + 1116, + 1117, + 1118, + 1119, + 1120, + 1121, + 1122, + 1123, + 1124, + 1125, + 1126, + 1127, + 1128, + 1129, + 1130, + 1131, + 1132, + 1133, + 1134, + 1135, + 1136, + 1137, + 1138, + 1139, + 1140, + 1141, + 1142, + 1143, + 1144, + 1145, + 1146, + 1147, + 1148, + 1149, + 1150, + 1151, + 1152, + 1153, + 1154, + 1155, + 1156, + 1157, + 1158, + 1159, + 1160, + 1161, + 1162, + 1163, + 1164, + 1165, + 1166, + 1167, + 1168, + 1169, + 1170, + 1171, + 1172, + 1173, + 1174, + 1175, + 1176, + 1177, + 1178, + 1179, + 1180, + 1181, + 1182, + 1183, + 1184, + 1185, + 1186, + 1187, + 1188, + 1189, + 1190, + 1191, + 1192, + 1193, + 1194, + 1195, + 1196, + 1197, + 1198, + 1199, + 1200, + 1201, + 1202, + 1203, + 1204, + 1205, + 1206, + 1207, + 1208, + 1209, + 1210, + 1211, + 1212, + 1213, + 1214, + 1215, + 1216, + 1217, + 1218, + 1219, + 1220, + 1221, + 1222, + 1223, + 1224, + 1225, + 1226, + 1227, + 1228, + 1229, + 1230, + 1231, + 1232, + 1233, + 1234, + 1235, + 1236, + 1237, + 1238, + 1239, + 1240, + 1241, + 1242, + 1243, + 1244, + 1245, + 1246, + 1247, + 1248, + 1249, + 1250, + 1251, + 1252, + 1253, + 1254, + 1255, + 1256, + 1257, + 1258, + 1259, + 1260, + 1261, + 1262, + 1263, + 1264, + 1265, + 1266, + 1267, + 1268, + 1269, + 1270, + 1271, + 1272, + 1273, + 1274, + 1275, + 1276, + 1277, + 1278, + 1279, + 1280, + 1281, + 1282, + 1283, + 1284, + 1285, + 1286, + 1287, + 1288, + 1289, + 1290, + 1291, + 1292, + 1293, + 1294, + 1295, + 1296, + 1297, + 1298, + 1299, + 1300, + 1301, + 1302, + 1303, + 1304, + 1305, + 1306, + 1307, + 1308, + 1309, + 1310, + 1311, + 1312, + 1313, + 1314, + 1315, + 1316, + 1317, + 1318, + 1319, + 1320, + 1321, + 1322, + 1323, + 1324, + 1325, + 1326, + 1327, + 1328, + 1329, + 1330, + 1331, + 1332, + 1333, + 1334, + 1335, + 1336, + 1337, + 1338, + 1339, + 1340, + 1341, + 1342, + 1343, + 1344, + 1345, + 1346, + 1347, + 1348, + 1349, + 1350, + 1351, + 1352, + 1353, + 1354, + 1355, + 1356, + 1357, + 1358, + 1359, + 1360, + 1361, + 1362, + 1363, + 1364, + 1365, + 1366, + 1367, + 1368, + 1369, + 1370, + 1371, + 1372, + 1373, + 1374, + 1375, + 1376, + 1377, + 1378, + 1379, + 1380, + 1381, + 1382, + 1383, + 1384, + 1385, + 1386, + 1387, + 1388, + 1389, + 1390, + 1391, + 1392, + 1393, + 1394, + 1395, + 1396, + 1397, + 1398, + 1399, + 1400, + 1401, + 1402, + 1403, + 1404, + 1405, + 1406, + 1407, + 1408, + 1409, + 1410, + 1411, + 1412, + 1413, + 1414, + 1415, + 1416, + 1417, + 1418, + 1419, + 1420, + 1421, + 1422, + 1423, + 1424, + 1425, + 1426, + 1427, + 1428, + 1429, + 1430, + 1431, + 1432, + 1433, + 1434, + 1435, + 1436, + 1437, + 1438, + 1439, + 1440, + 1441, + 1442, + 1443, + 1444, + 1445, + 1446, + 1447, + 1448, + 1449, + 1450, + 1451, + 1452, + 1453, + 1454, + 1455, + 1456, + 1457, + 1458, + 1459, + 1460, + 1461, + 1462, + 1463, + 1464, + 1465, + 1466, + 1467, + 1468, + 1469, + 1470, + 1471, + 1472, + 1473, + 1474, + 1475, + 1476, + 1477, + 1478, + 1479, + 1480, + 1481, + 1482, + 1483, + 1484, + 1485, + 1486, + 1487, + 1488, + 1489, + 1490, + 1491, + 1492, + 1493, + 1494, + 1495, + 1496, + 1497, + 1498, + 1499, + 1500, + 1501, + 1502, + 1503, + 1504, + 1505, + 1506, + 1507, + 1508, + 1509, + 1510, + 1511, + 1512, + 1513, + 1514, + 1515, + 1516, + 1517, + 1518, + 1519, + 1520, + 1521, + 1522, + 1523, + 1524, + 1525, + 1526, + 1527, + 1528, + 1529, + 1530, + 1531, + 1532, + 1533, + 1534, + 1535, + 1536, + 1537, + 1538, + 1539, + 1540, + 1541, + 1542, + 1543, + 1544, + 1545, + 1546, + 1547, + 1548, + 1549, + 1550, + 1551, + 1552, + 1553, + 1554, + 1555, + 1556, + 1557, + 1558, + 1559, + 1560, + 1561, + 1562, + 1563, + 1564, + 1565, + 1566, + 1567, + 1568, + 1569, + 1570, + 1571, + 1572, + 1573, + 1574, + 1575, + 1576, + 1577, + 1578, + 1579, + 1580, + 1581, + 1582, + 1583, + 1584, + 1585, + 1586, + 1587, + 1588, + 1589, + 1590, + 1591, + 1592, + 1593, + 1594, + 1595, + 1596, + 1597, + 1598, + 1599, + 1600, + 1601, + 1602, + 1603, + 1604, + 1605, + 1606, + 1607, + 1608, + 1609, + 1610, + 1611, + 1612, + 1613, + 1614, + 1615, + 1616, + 1617, + 1618, + 1619, + 1620, + 1621, + 1622, + 1623, + 1624, + 1625, + 1626, + 1627, + 1628, + 1629, + 1630, + 1631, + 1632, + 1633, + 1634, + 1635, + 1636, + 1637, + 1638, + 1639, + 1640, + 1641, + 1642, + 1643, + 1644, + 1645, + 1646, + 1647, + 1648, + 1649, + 1650, + 1651, + 1652, + 1653, + 1654, + 1655, + 1656, + 1657, + 1658, + 1659, + 1660, + 1661, + 1662, + 1663, + 1664, + 1665, + 1666, + 1667, + 1668, + 1669, + 1670, + 1671, + 1672, + 1673, + 1674, + 1675, + 1676, + 1677, + 1678, + 1679, + 1680, + 1681, + 1682, + 1683, + 1684, + 1685, + 1686, + 1687, + 1688, + 1689, + 1690, + 1691, + 1692, + 1693, + 1694, + 1695, + 1696, + 1697, + 1698, + 1699, + 1700, + 1701, + 1702, + 1703, + 1704, + 1705, + 1706, + 1707, + 1708, + 1709, + 1710, + 1711, + 1712, + 1713, + 1714, + 1715, + 1716, + 1717, + 1718, + 1719, + 1720, + 1721, + 1722, + 1723, + 1724, + 1725, + 1726, + 1727, + 1728, + 1729, + 1730, + 1731, + 1732, + 1733, + 1734, + 1735, + 1736, + 1737, + 1738, + 1739, + 1740, + 1741, + 1742, + 1743, + 1744, + 1745, + 1746, + 1747, + 1748, + 1749, + 1750, + 1751, + 1752, + 1753, + 1754, + 1755, + 1756, + 1757, + 1758, + 1759, + 1760, + 1761, + 1762, + 1763, + 1764, + 1765, + 1766, + 1767, + 1768, + 1769, + 1770, + 1771, + 1772, + 1773, + 1774, + 1775, + 1776, + 1777, + 1778, + 1779, + 1780, + 1781, + 1782, + 1783, + 1784, + 1785, + 1786, + 1787, + 1788, + 1789, + 1790, + 1791, + 1792, + 1793, + 1794, + 1795, + 1796, + 1797, + 1798, + 1799, + 1800, + 1801, + 1802, + 1803, + 1804, + 1805, + 1806, + 1807, + 1808, + 1809, + 1810, + 1811, + 1812, + 1813, + 1814, + 1815, + 1816, + 1817, + 1818, + 1819, + 1820, + 1821, + 1822, + 1823, + 1824, + 1825, + 1826, + 1827, + 1828, + 1829, + 1830, + 1831, + 1832, + 1833, + 1834, + 1835, + 1836, + 1837, + 1838, + 1839, + 1840, + 1841, + 1842, + 1843, + 1844, + 1845, + 1846, + 1847, + 1848, + 1849, + 1850, + 1851, + 1852, + 1853, + 1854, + 1855, + 1856, + 1857, + 1858, + 1859, + 1860, + 1861, + 1862, + 1863, + 1864, + 1865, + 1866, + 1867, + 1868, + 1869, + 1870, + 1871, + 1872, + 1873, + 1874, + 1875, + 1876, + 1877, + 1878, + 1879, + 1880, + 1881, + 1882, + 1883, + 1884, + 1885, + 1886, + 1887, + 1888, + 1889, + 1890, + 1891, + 1892, + 1893, + 1894, + 1895, + 1896, + 1897, + 1898, + 1899, + 1900, + 1901, + 1902, + 1903, + 1904, + 1905, + 1906, + 1907, + 1908, + 1909, + 1910, + 1911, + 1912, + 1913, + 1914, + 1915, + 1916, + 1917, + 1918, + 1919, + 1920, + 1921, + 1922, + 1923, + 1924, + 1925, + 1926, + 1927, + 1928, + 1929, + 1930, + 1931, + 1932, + 1933, + 1934, + 1935, + 1936, + 1937, + 1938, + 1939, + 1940, + 1941, + 1942, + 1943, + 1944, + 1945, + 1946, + 1947, + 1948, + 1949, + 1950, + 1951, + 1952, + 1953, + 1954, + 1955, + 1956, + 1957, + 1958, + 1959, + 1960, + 1961, + 1962, + 1963, + 1964, + 1965, + 1966, + 1967, + 1968, + 1969, + 1970, + 1971, + 1972, + 1973, + 1974, + 1975, + 1976, + 1977, + 1978, + 1979, + 1980, + 1981, + 1982, + 1983, + 1984, + 1985, + 1986, + 1987, + 1988, + 1989, + 1990, + 1991, + 1992, + 1993, + 1994, + 1995, + 1996, + 1997, + 1998, + 1999, + 2000, + 2001, + 2002, + 2003, + 2004, + 2005, + 2006, + 2007, + 2008, + 2009, + 2010, + 2011, + 2012, + 2013, + 2014, + 2015, + 2016, + 2017, + 2018, + 2019, + 2020, + 2021, + 2022, + 2023, + 2024, + 2025, + 2026, + 2027, + 2028, + 2029, + 2030, + 2031, + 2032, + 2033, + 2034, + 2035, + 2036, + 2037, + 2038, + 2039, + 2040, + 2041, + 2042, + 2043, + 2044, + 2045, + 2046, + 2047, + 2048, + 2049, + 2050, + 2051, + 2052, + 2053, + 2054, + 2055, + 2056, + 2057, + 2058, + 2059, + 2060, + 2061, + 2062, + 2063, + 2064, + 2065, + 2066, + 2067, + 2068, + 2069, + 2070, + 2071, + 2072, + 2073, + 2074, + 2075, + 2076, + 2077, + 2078, + 2079, + 2080, + 2081, + 2082, + 2083, + 2084, + 2085, + 2086, + 2087, + 2088, + 2089, + 2090, + 2091, + 2092, + 2093, + 2094, + 2095, + 2096, + 2097, + 2098, + 2099, + 2100, + 2101, + 2102, + 2103, + 2104, + 2105, + 2106, + 2107, + 2108, + 2109, + 2110, + 2111, + 2112, + 2113, + 2114, + 2115, + 2116, + 2117, + 2118, + 2119, + 2120, + 2121, + 2122, + 2123, + 2124, + 2125, + 2126, + 2127, + 2128, + 2129, + 2130, + 2131, + 2132, + 2133, + 2134, + 2135, + 2136, + 2137, + 2138, + 2139, + 2140, + 2141, + 2142, + 2143, + 2144, + 2145, + 2146, + 2147, + 2148, + 2149, + 2150, + 2151, + 2152, + 2153, + 2154, + 2155, + 2156, + 2157, + 2158, + 2159, + 2160, + 2161, + 2162, + 2163, + 2164, + 2165, + 2166, + 2167, + 2168, + 2169, + 2170, + 2171, + 2172, + 2173, + 2174, + 2175, + 2176, + 2177, + 2178, + 2179, + 2180, + 2181, + 2182, + 2183, + 2184, + 2185, + 2186, + 2187, + 2188, + 2189, + 2190, + 2191, + 2192, + 2193, + 2194, + 2195, + 2196, + 2197, + 2198, + 2199, + 2200, + 2201, + 2202, + 2203, + 2204, + 2205, + 2206, + 2207, + 2208, + 2209, + 2210, + 2211, + 2212, + 2213, + 2214, + 2215, + 2216, + 2217, + 2218, + 2219, + 2220, + 2221, + 2222, + 2223, + 2224, + 2225, + 2226, + 2227, + 2228, + 2229, + 2230, + 2231, + 2232, + 2233, + 2234, + 2235, + 2236, + 2237, + 2238, + 2239, + 2240, + 2241, + 2242, + 2243, + 2244, + 2245, + 2255, + 2260, + 2265, + 2270, + 2300 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/325_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/325_Buckets.json new file mode 100644 index 0000000000000..474fd26cc7bad --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/325_Buckets.json @@ -0,0 +1,6506 @@ +{ + "counts": [ + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 2, + 2, + 1, + 3, + 3, + 2, + 2, + 1 + ], + "values": [ + 5.5, + 6, + 6.5, + 7, + 7.5, + 8, + 8.5, + 9, + 9.5, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 475, + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493, + 494, + 495, + 496, + 497, + 498, + 499, + 500, + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 534, + 535, + 536, + 537, + 538, + 539, + 540, + 541, + 542, + 543, + 544, + 545, + 546, + 547, + 548, + 549, + 550, + 551, + 552, + 553, + 554, + 555, + 556, + 557, + 558, + 559, + 560, + 561, + 562, + 563, + 564, + 565, + 566, + 567, + 568, + 569, + 570, + 571, + 572, + 573, + 574, + 575, + 576, + 577, + 578, + 579, + 580, + 581, + 582, + 583, + 584, + 585, + 586, + 587, + 588, + 589, + 590, + 591, + 592, + 593, + 594, + 595, + 596, + 597, + 598, + 599, + 600, + 601, + 602, + 603, + 604, + 605, + 606, + 607, + 608, + 609, + 610, + 611, + 612, + 613, + 614, + 615, + 616, + 617, + 618, + 619, + 620, + 621, + 622, + 623, + 624, + 625, + 626, + 627, + 628, + 629, + 630, + 631, + 632, + 633, + 634, + 635, + 636, + 637, + 638, + 639, + 640, + 641, + 642, + 643, + 644, + 645, + 646, + 647, + 648, + 649, + 650, + 651, + 652, + 653, + 654, + 655, + 656, + 657, + 658, + 659, + 660, + 661, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 671, + 672, + 673, + 674, + 675, + 676, + 677, + 678, + 679, + 680, + 681, + 682, + 683, + 684, + 685, + 686, + 687, + 688, + 689, + 690, + 691, + 692, + 693, + 694, + 695, + 696, + 697, + 698, + 699, + 700, + 701, + 702, + 703, + 704, + 705, + 706, + 707, + 708, + 709, + 710, + 711, + 712, + 713, + 714, + 715, + 716, + 717, + 718, + 719, + 720, + 721, + 722, + 723, + 724, + 725, + 726, + 727, + 728, + 729, + 730, + 731, + 732, + 733, + 734, + 735, + 736, + 737, + 738, + 739, + 740, + 741, + 742, + 743, + 744, + 745, + 746, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 755, + 756, + 757, + 758, + 759, + 760, + 761, + 762, + 763, + 764, + 765, + 766, + 767, + 768, + 769, + 770, + 771, + 772, + 773, + 774, + 775, + 776, + 777, + 778, + 779, + 780, + 781, + 782, + 783, + 784, + 785, + 786, + 787, + 788, + 789, + 790, + 791, + 792, + 793, + 794, + 795, + 796, + 797, + 798, + 799, + 800, + 801, + 802, + 803, + 804, + 805, + 806, + 807, + 808, + 809, + 810, + 811, + 812, + 813, + 814, + 815, + 816, + 817, + 818, + 819, + 820, + 821, + 822, + 823, + 824, + 825, + 826, + 827, + 828, + 829, + 830, + 831, + 832, + 833, + 834, + 835, + 836, + 837, + 838, + 839, + 840, + 841, + 842, + 843, + 844, + 845, + 846, + 847, + 848, + 849, + 850, + 851, + 852, + 853, + 854, + 855, + 856, + 857, + 858, + 859, + 860, + 861, + 862, + 863, + 864, + 865, + 866, + 867, + 868, + 869, + 870, + 871, + 872, + 873, + 874, + 875, + 876, + 877, + 878, + 879, + 880, + 881, + 882, + 883, + 884, + 885, + 886, + 887, + 888, + 889, + 890, + 891, + 892, + 893, + 894, + 895, + 896, + 897, + 898, + 899, + 900, + 901, + 902, + 903, + 904, + 905, + 906, + 907, + 908, + 909, + 910, + 911, + 912, + 913, + 914, + 915, + 916, + 917, + 918, + 919, + 920, + 921, + 922, + 923, + 924, + 925, + 926, + 927, + 928, + 929, + 930, + 931, + 932, + 933, + 934, + 935, + 936, + 937, + 938, + 939, + 940, + 941, + 942, + 943, + 944, + 945, + 946, + 947, + 948, + 949, + 950, + 951, + 952, + 953, + 954, + 955, + 956, + 957, + 958, + 959, + 960, + 961, + 962, + 963, + 964, + 965, + 966, + 967, + 968, + 969, + 970, + 971, + 972, + 973, + 974, + 975, + 976, + 977, + 978, + 979, + 980, + 981, + 982, + 983, + 984, + 985, + 986, + 987, + 988, + 989, + 990, + 991, + 992, + 993, + 994, + 995, + 996, + 997, + 998, + 999, + 1000, + 1001, + 1002, + 1003, + 1004, + 1005, + 1006, + 1007, + 1008, + 1009, + 1010, + 1011, + 1012, + 1013, + 1014, + 1015, + 1016, + 1017, + 1018, + 1019, + 1020, + 1021, + 1022, + 1023, + 1024, + 1025, + 1026, + 1027, + 1028, + 1029, + 1030, + 1031, + 1032, + 1033, + 1034, + 1035, + 1036, + 1037, + 1038, + 1039, + 1040, + 1041, + 1042, + 1043, + 1044, + 1045, + 1046, + 1047, + 1048, + 1049, + 1050, + 1051, + 1052, + 1053, + 1054, + 1055, + 1056, + 1057, + 1058, + 1059, + 1060, + 1061, + 1062, + 1063, + 1064, + 1065, + 1066, + 1067, + 1068, + 1069, + 1070, + 1071, + 1072, + 1073, + 1074, + 1075, + 1076, + 1077, + 1078, + 1079, + 1080, + 1081, + 1082, + 1083, + 1084, + 1085, + 1086, + 1087, + 1088, + 1089, + 1090, + 1091, + 1092, + 1093, + 1094, + 1095, + 1096, + 1097, + 1098, + 1099, + 1100, + 1101, + 1102, + 1103, + 1104, + 1105, + 1106, + 1107, + 1108, + 1109, + 1110, + 1111, + 1112, + 1113, + 1114, + 1115, + 1116, + 1117, + 1118, + 1119, + 1120, + 1121, + 1122, + 1123, + 1124, + 1125, + 1126, + 1127, + 1128, + 1129, + 1130, + 1131, + 1132, + 1133, + 1134, + 1135, + 1136, + 1137, + 1138, + 1139, + 1140, + 1141, + 1142, + 1143, + 1144, + 1145, + 1146, + 1147, + 1148, + 1149, + 1150, + 1151, + 1152, + 1153, + 1154, + 1155, + 1156, + 1157, + 1158, + 1159, + 1160, + 1161, + 1162, + 1163, + 1164, + 1165, + 1166, + 1167, + 1168, + 1169, + 1170, + 1171, + 1172, + 1173, + 1174, + 1175, + 1176, + 1177, + 1178, + 1179, + 1180, + 1181, + 1182, + 1183, + 1184, + 1185, + 1186, + 1187, + 1188, + 1189, + 1190, + 1191, + 1192, + 1193, + 1194, + 1195, + 1196, + 1197, + 1198, + 1199, + 1200, + 1201, + 1202, + 1203, + 1204, + 1205, + 1206, + 1207, + 1208, + 1209, + 1210, + 1211, + 1212, + 1213, + 1214, + 1215, + 1216, + 1217, + 1218, + 1219, + 1220, + 1221, + 1222, + 1223, + 1224, + 1225, + 1226, + 1227, + 1228, + 1229, + 1230, + 1231, + 1232, + 1233, + 1234, + 1235, + 1236, + 1237, + 1238, + 1239, + 1240, + 1241, + 1242, + 1243, + 1244, + 1245, + 1246, + 1247, + 1248, + 1249, + 1250, + 1251, + 1252, + 1253, + 1254, + 1255, + 1256, + 1257, + 1258, + 1259, + 1260, + 1261, + 1262, + 1263, + 1264, + 1265, + 1266, + 1267, + 1268, + 1269, + 1270, + 1271, + 1272, + 1273, + 1274, + 1275, + 1276, + 1277, + 1278, + 1279, + 1280, + 1281, + 1282, + 1283, + 1284, + 1285, + 1286, + 1287, + 1288, + 1289, + 1290, + 1291, + 1292, + 1293, + 1294, + 1295, + 1296, + 1297, + 1298, + 1299, + 1300, + 1301, + 1302, + 1303, + 1304, + 1305, + 1306, + 1307, + 1308, + 1309, + 1310, + 1311, + 1312, + 1313, + 1314, + 1315, + 1316, + 1317, + 1318, + 1319, + 1320, + 1321, + 1322, + 1323, + 1324, + 1325, + 1326, + 1327, + 1328, + 1329, + 1330, + 1331, + 1332, + 1333, + 1334, + 1335, + 1336, + 1337, + 1338, + 1339, + 1340, + 1341, + 1342, + 1343, + 1344, + 1345, + 1346, + 1347, + 1348, + 1349, + 1350, + 1351, + 1352, + 1353, + 1354, + 1355, + 1356, + 1357, + 1358, + 1359, + 1360, + 1361, + 1362, + 1363, + 1364, + 1365, + 1366, + 1367, + 1368, + 1369, + 1370, + 1371, + 1372, + 1373, + 1374, + 1375, + 1376, + 1377, + 1378, + 1379, + 1380, + 1381, + 1382, + 1383, + 1384, + 1385, + 1386, + 1387, + 1388, + 1389, + 1390, + 1391, + 1392, + 1393, + 1394, + 1395, + 1396, + 1397, + 1398, + 1399, + 1400, + 1401, + 1402, + 1403, + 1404, + 1405, + 1406, + 1407, + 1408, + 1409, + 1410, + 1411, + 1412, + 1413, + 1414, + 1415, + 1416, + 1417, + 1418, + 1419, + 1420, + 1421, + 1422, + 1423, + 1424, + 1425, + 1426, + 1427, + 1428, + 1429, + 1430, + 1431, + 1432, + 1433, + 1434, + 1435, + 1436, + 1437, + 1438, + 1439, + 1440, + 1441, + 1442, + 1443, + 1444, + 1445, + 1446, + 1447, + 1448, + 1449, + 1450, + 1451, + 1452, + 1453, + 1454, + 1455, + 1456, + 1457, + 1458, + 1459, + 1460, + 1461, + 1462, + 1463, + 1464, + 1465, + 1466, + 1467, + 1468, + 1469, + 1470, + 1471, + 1472, + 1473, + 1474, + 1475, + 1476, + 1477, + 1478, + 1479, + 1480, + 1481, + 1482, + 1483, + 1484, + 1485, + 1486, + 1487, + 1488, + 1489, + 1490, + 1491, + 1492, + 1493, + 1494, + 1495, + 1496, + 1497, + 1498, + 1499, + 1500, + 1501, + 1502, + 1503, + 1504, + 1505, + 1506, + 1507, + 1508, + 1509, + 1510, + 1511, + 1512, + 1513, + 1514, + 1515, + 1516, + 1517, + 1518, + 1519, + 1520, + 1521, + 1522, + 1523, + 1524, + 1525, + 1526, + 1527, + 1528, + 1529, + 1530, + 1531, + 1532, + 1533, + 1534, + 1535, + 1536, + 1537, + 1538, + 1539, + 1540, + 1541, + 1542, + 1543, + 1544, + 1545, + 1546, + 1547, + 1548, + 1549, + 1550, + 1551, + 1552, + 1553, + 1554, + 1555, + 1556, + 1557, + 1558, + 1559, + 1560, + 1561, + 1562, + 1563, + 1564, + 1565, + 1566, + 1567, + 1568, + 1569, + 1570, + 1571, + 1572, + 1573, + 1574, + 1575, + 1576, + 1577, + 1578, + 1579, + 1580, + 1581, + 1582, + 1583, + 1584, + 1585, + 1586, + 1587, + 1588, + 1589, + 1590, + 1591, + 1592, + 1593, + 1594, + 1595, + 1596, + 1597, + 1598, + 1599, + 1600, + 1601, + 1602, + 1603, + 1604, + 1605, + 1606, + 1607, + 1608, + 1609, + 1610, + 1611, + 1612, + 1613, + 1614, + 1615, + 1616, + 1617, + 1618, + 1619, + 1620, + 1621, + 1622, + 1623, + 1624, + 1625, + 1626, + 1627, + 1628, + 1629, + 1630, + 1631, + 1632, + 1633, + 1634, + 1635, + 1636, + 1637, + 1638, + 1639, + 1640, + 1641, + 1642, + 1643, + 1644, + 1645, + 1646, + 1647, + 1648, + 1649, + 1650, + 1651, + 1652, + 1653, + 1654, + 1655, + 1656, + 1657, + 1658, + 1659, + 1660, + 1661, + 1662, + 1663, + 1664, + 1665, + 1666, + 1667, + 1668, + 1669, + 1670, + 1671, + 1672, + 1673, + 1674, + 1675, + 1676, + 1677, + 1678, + 1679, + 1680, + 1681, + 1682, + 1683, + 1684, + 1685, + 1686, + 1687, + 1688, + 1689, + 1690, + 1691, + 1692, + 1693, + 1694, + 1695, + 1696, + 1697, + 1698, + 1699, + 1700, + 1701, + 1702, + 1703, + 1704, + 1705, + 1706, + 1707, + 1708, + 1709, + 1710, + 1711, + 1712, + 1713, + 1714, + 1715, + 1716, + 1717, + 1718, + 1719, + 1720, + 1721, + 1722, + 1723, + 1724, + 1725, + 1726, + 1727, + 1728, + 1729, + 1730, + 1731, + 1732, + 1733, + 1734, + 1735, + 1736, + 1737, + 1738, + 1739, + 1740, + 1741, + 1742, + 1743, + 1744, + 1745, + 1746, + 1747, + 1748, + 1749, + 1750, + 1751, + 1752, + 1753, + 1754, + 1755, + 1756, + 1757, + 1758, + 1759, + 1760, + 1761, + 1762, + 1763, + 1764, + 1765, + 1766, + 1767, + 1768, + 1769, + 1770, + 1771, + 1772, + 1773, + 1774, + 1775, + 1776, + 1777, + 1778, + 1779, + 1780, + 1781, + 1782, + 1783, + 1784, + 1785, + 1786, + 1787, + 1788, + 1789, + 1790, + 1791, + 1792, + 1793, + 1794, + 1795, + 1796, + 1797, + 1798, + 1799, + 1800, + 1801, + 1802, + 1803, + 1804, + 1805, + 1806, + 1807, + 1808, + 1809, + 1810, + 1811, + 1812, + 1813, + 1814, + 1815, + 1816, + 1817, + 1818, + 1819, + 1820, + 1821, + 1822, + 1823, + 1824, + 1825, + 1826, + 1827, + 1828, + 1829, + 1830, + 1831, + 1832, + 1833, + 1834, + 1835, + 1836, + 1837, + 1838, + 1839, + 1840, + 1841, + 1842, + 1843, + 1844, + 1845, + 1846, + 1847, + 1848, + 1849, + 1850, + 1851, + 1852, + 1853, + 1854, + 1855, + 1856, + 1857, + 1858, + 1859, + 1860, + 1861, + 1862, + 1863, + 1864, + 1865, + 1866, + 1867, + 1868, + 1869, + 1870, + 1871, + 1872, + 1873, + 1874, + 1875, + 1876, + 1877, + 1878, + 1879, + 1880, + 1881, + 1882, + 1883, + 1884, + 1885, + 1886, + 1887, + 1888, + 1889, + 1890, + 1891, + 1892, + 1893, + 1894, + 1895, + 1896, + 1897, + 1898, + 1899, + 1900, + 1901, + 1902, + 1903, + 1904, + 1905, + 1906, + 1907, + 1908, + 1909, + 1910, + 1911, + 1912, + 1913, + 1914, + 1915, + 1916, + 1917, + 1918, + 1919, + 1920, + 1921, + 1922, + 1923, + 1924, + 1925, + 1926, + 1927, + 1928, + 1929, + 1930, + 1931, + 1932, + 1933, + 1934, + 1935, + 1936, + 1937, + 1938, + 1939, + 1940, + 1941, + 1942, + 1943, + 1944, + 1945, + 1946, + 1947, + 1948, + 1949, + 1950, + 1951, + 1952, + 1953, + 1954, + 1955, + 1956, + 1957, + 1958, + 1959, + 1960, + 1961, + 1962, + 1963, + 1964, + 1965, + 1966, + 1967, + 1968, + 1969, + 1970, + 1971, + 1972, + 1973, + 1974, + 1975, + 1976, + 1977, + 1978, + 1979, + 1980, + 1981, + 1982, + 1983, + 1984, + 1985, + 1986, + 1987, + 1988, + 1989, + 1990, + 1991, + 1992, + 1993, + 1994, + 1995, + 1996, + 1997, + 1998, + 1999, + 2000, + 2001, + 2002, + 2003, + 2004, + 2005, + 2006, + 2007, + 2008, + 2009, + 2010, + 2011, + 2012, + 2013, + 2014, + 2015, + 2016, + 2017, + 2018, + 2019, + 2020, + 2021, + 2022, + 2023, + 2024, + 2025, + 2026, + 2027, + 2028, + 2029, + 2030, + 2031, + 2032, + 2033, + 2034, + 2035, + 2036, + 2037, + 2038, + 2039, + 2040, + 2041, + 2042, + 2043, + 2044, + 2045, + 2046, + 2047, + 2048, + 2049, + 2050, + 2051, + 2052, + 2053, + 2054, + 2055, + 2056, + 2057, + 2058, + 2059, + 2060, + 2061, + 2062, + 2063, + 2064, + 2065, + 2066, + 2067, + 2068, + 2069, + 2070, + 2071, + 2072, + 2073, + 2074, + 2075, + 2076, + 2077, + 2078, + 2079, + 2080, + 2081, + 2082, + 2083, + 2084, + 2085, + 2086, + 2087, + 2088, + 2089, + 2090, + 2091, + 2092, + 2093, + 2094, + 2095, + 2096, + 2097, + 2098, + 2099, + 2100, + 2101, + 2102, + 2103, + 2104, + 2105, + 2106, + 2107, + 2108, + 2109, + 2110, + 2111, + 2112, + 2113, + 2114, + 2115, + 2116, + 2117, + 2118, + 2119, + 2120, + 2121, + 2122, + 2123, + 2124, + 2125, + 2126, + 2127, + 2128, + 2129, + 2130, + 2131, + 2132, + 2133, + 2134, + 2135, + 2136, + 2137, + 2138, + 2139, + 2140, + 2141, + 2142, + 2143, + 2144, + 2145, + 2146, + 2147, + 2148, + 2149, + 2150, + 2151, + 2152, + 2153, + 2154, + 2155, + 2156, + 2157, + 2158, + 2159, + 2160, + 2161, + 2162, + 2163, + 2164, + 2165, + 2166, + 2167, + 2168, + 2169, + 2170, + 2171, + 2172, + 2173, + 2174, + 2175, + 2176, + 2177, + 2178, + 2179, + 2180, + 2181, + 2182, + 2183, + 2184, + 2185, + 2186, + 2187, + 2188, + 2189, + 2190, + 2191, + 2192, + 2193, + 2194, + 2195, + 2196, + 2197, + 2198, + 2199, + 2200, + 2201, + 2202, + 2203, + 2204, + 2205, + 2206, + 2207, + 2208, + 2209, + 2210, + 2211, + 2212, + 2213, + 2214, + 2215, + 2216, + 2217, + 2218, + 2219, + 2220, + 2221, + 2222, + 2223, + 2224, + 2225, + 2226, + 2227, + 2228, + 2229, + 2230, + 2231, + 2232, + 2233, + 2234, + 2235, + 2236, + 2237, + 2238, + 2239, + 2240, + 2241, + 2242, + 2243, + 2244, + 2245, + 2246, + 2247, + 2248, + 2249, + 2250, + 2251, + 2252, + 2253, + 2254, + 2255, + 2256, + 2257, + 2258, + 2259, + 2260, + 2261, + 2262, + 2263, + 2264, + 2265, + 2266, + 2267, + 2268, + 2269, + 2270, + 2271, + 2272, + 2273, + 2274, + 2275, + 2276, + 2277, + 2278, + 2279, + 2280, + 2281, + 2282, + 2283, + 2284, + 2285, + 2286, + 2287, + 2288, + 2289, + 2290, + 2291, + 2292, + 2293, + 2294, + 2295, + 2296, + 2297, + 2298, + 2299, + 2300, + 2301, + 2302, + 2303, + 2304, + 2305, + 2306, + 2307, + 2308, + 2309, + 2310, + 2311, + 2312, + 2313, + 2314, + 2315, + 2316, + 2317, + 2318, + 2319, + 2320, + 2321, + 2322, + 2323, + 2324, + 2325, + 2326, + 2327, + 2328, + 2329, + 2330, + 2331, + 2332, + 2333, + 2334, + 2335, + 2336, + 2337, + 2338, + 2339, + 2340, + 2341, + 2342, + 2343, + 2344, + 2345, + 2346, + 2347, + 2348, + 2349, + 2350, + 2351, + 2352, + 2353, + 2354, + 2355, + 2356, + 2357, + 2358, + 2359, + 2360, + 2361, + 2362, + 2363, + 2364, + 2365, + 2366, + 2367, + 2368, + 2369, + 2370, + 2371, + 2372, + 2373, + 2374, + 2375, + 2376, + 2377, + 2378, + 2379, + 2380, + 2381, + 2382, + 2383, + 2384, + 2385, + 2386, + 2387, + 2388, + 2389, + 2390, + 2391, + 2392, + 2393, + 2394, + 2395, + 2396, + 2397, + 2398, + 2399, + 2400, + 2401, + 2402, + 2403, + 2404, + 2405, + 2406, + 2407, + 2408, + 2409, + 2410, + 2411, + 2412, + 2413, + 2414, + 2415, + 2416, + 2417, + 2418, + 2419, + 2420, + 2421, + 2422, + 2423, + 2424, + 2425, + 2426, + 2427, + 2428, + 2429, + 2430, + 2431, + 2432, + 2433, + 2434, + 2435, + 2436, + 2437, + 2438, + 2439, + 2440, + 2441, + 2442, + 2443, + 2444, + 2445, + 2446, + 2447, + 2448, + 2449, + 2450, + 2451, + 2452, + 2453, + 2454, + 2455, + 2456, + 2457, + 2458, + 2459, + 2460, + 2461, + 2462, + 2463, + 2464, + 2465, + 2466, + 2467, + 2468, + 2469, + 2470, + 2471, + 2472, + 2473, + 2474, + 2475, + 2476, + 2477, + 2478, + 2479, + 2480, + 2481, + 2482, + 2483, + 2484, + 2485, + 2486, + 2487, + 2488, + 2489, + 2490, + 2491, + 2492, + 2493, + 2494, + 2495, + 2496, + 2497, + 2498, + 2499, + 2500, + 2501, + 2502, + 2503, + 2504, + 2505, + 2506, + 2507, + 2508, + 2509, + 2510, + 2511, + 2512, + 2513, + 2514, + 2515, + 2516, + 2517, + 2518, + 2519, + 2520, + 2521, + 2522, + 2523, + 2524, + 2525, + 2526, + 2527, + 2528, + 2529, + 2530, + 2531, + 2532, + 2533, + 2534, + 2535, + 2536, + 2537, + 2538, + 2539, + 2540, + 2541, + 2542, + 2543, + 2544, + 2545, + 2546, + 2547, + 2548, + 2549, + 2550, + 2551, + 2552, + 2553, + 2554, + 2555, + 2556, + 2557, + 2558, + 2559, + 2560, + 2561, + 2562, + 2563, + 2564, + 2565, + 2566, + 2567, + 2568, + 2569, + 2570, + 2571, + 2572, + 2573, + 2574, + 2575, + 2576, + 2577, + 2578, + 2579, + 2580, + 2581, + 2582, + 2583, + 2584, + 2585, + 2586, + 2587, + 2588, + 2589, + 2590, + 2591, + 2592, + 2593, + 2594, + 2595, + 2596, + 2597, + 2598, + 2599, + 2600, + 2601, + 2602, + 2603, + 2604, + 2605, + 2606, + 2607, + 2608, + 2609, + 2610, + 2611, + 2612, + 2613, + 2614, + 2615, + 2616, + 2617, + 2618, + 2619, + 2620, + 2621, + 2622, + 2623, + 2624, + 2625, + 2626, + 2627, + 2628, + 2629, + 2630, + 2631, + 2632, + 2633, + 2634, + 2635, + 2636, + 2637, + 2638, + 2639, + 2640, + 2641, + 2642, + 2643, + 2644, + 2645, + 2646, + 2647, + 2648, + 2649, + 2650, + 2651, + 2652, + 2653, + 2654, + 2655, + 2656, + 2657, + 2658, + 2659, + 2660, + 2661, + 2662, + 2663, + 2664, + 2665, + 2666, + 2667, + 2668, + 2669, + 2670, + 2671, + 2672, + 2673, + 2674, + 2675, + 2676, + 2677, + 2678, + 2679, + 2680, + 2681, + 2682, + 2683, + 2684, + 2685, + 2686, + 2687, + 2688, + 2689, + 2690, + 2691, + 2692, + 2693, + 2694, + 2695, + 2696, + 2697, + 2698, + 2699, + 2700, + 2701, + 2702, + 2703, + 2704, + 2705, + 2706, + 2707, + 2708, + 2709, + 2710, + 2711, + 2712, + 2713, + 2714, + 2715, + 2716, + 2717, + 2718, + 2719, + 2720, + 2721, + 2722, + 2723, + 2724, + 2725, + 2726, + 2727, + 2728, + 2729, + 2730, + 2731, + 2732, + 2733, + 2734, + 2735, + 2736, + 2737, + 2738, + 2739, + 2740, + 2741, + 2742, + 2743, + 2744, + 2745, + 2746, + 2747, + 2748, + 2749, + 2750, + 2751, + 2752, + 2753, + 2754, + 2755, + 2756, + 2757, + 2758, + 2759, + 2760, + 2761, + 2762, + 2763, + 2764, + 2765, + 2766, + 2767, + 2768, + 2769, + 2770, + 2771, + 2772, + 2773, + 2774, + 2775, + 2776, + 2777, + 2778, + 2779, + 2780, + 2781, + 2782, + 2783, + 2784, + 2785, + 2786, + 2787, + 2788, + 2789, + 2790, + 2791, + 2792, + 2793, + 2794, + 2795, + 2796, + 2797, + 2798, + 2799, + 2800, + 2801, + 2802, + 2803, + 2804, + 2805, + 2806, + 2807, + 2808, + 2809, + 2810, + 2811, + 2812, + 2813, + 2814, + 2815, + 2816, + 2817, + 2818, + 2819, + 2820, + 2821, + 2822, + 2823, + 2824, + 2825, + 2826, + 2827, + 2828, + 2829, + 2830, + 2831, + 2832, + 2833, + 2834, + 2835, + 2836, + 2837, + 2838, + 2839, + 2840, + 2841, + 2842, + 2843, + 2844, + 2845, + 2846, + 2847, + 2848, + 2849, + 2850, + 2851, + 2852, + 2853, + 2854, + 2855, + 2856, + 2857, + 2858, + 2859, + 2860, + 2861, + 2862, + 2863, + 2864, + 2865, + 2866, + 2867, + 2868, + 2869, + 2870, + 2871, + 2872, + 2873, + 2874, + 2875, + 2876, + 2877, + 2878, + 2879, + 2880, + 2881, + 2882, + 2883, + 2884, + 2885, + 2886, + 2887, + 2888, + 2889, + 2890, + 2891, + 2892, + 2893, + 2894, + 2895, + 2896, + 2897, + 2898, + 2899, + 2900, + 2901, + 2902, + 2903, + 2904, + 2905, + 2906, + 2907, + 2908, + 2909, + 2910, + 2911, + 2912, + 2913, + 2914, + 2915, + 2916, + 2917, + 2918, + 2919, + 2920, + 2921, + 2922, + 2923, + 2924, + 2925, + 2926, + 2927, + 2928, + 2929, + 2930, + 2931, + 2932, + 2933, + 2934, + 2935, + 2936, + 2937, + 2938, + 2939, + 2940, + 2941, + 2942, + 2943, + 2944, + 2945, + 2946, + 2947, + 2948, + 2949, + 2950, + 2951, + 2952, + 2953, + 2954, + 2955, + 2956, + 2957, + 2958, + 2959, + 2960, + 2961, + 2962, + 2963, + 2964, + 2965, + 2966, + 2967, + 2968, + 2969, + 2970, + 2971, + 2972, + 2973, + 2974, + 2975, + 2976, + 2977, + 2978, + 2979, + 2980, + 2981, + 2982, + 2983, + 2984, + 2985, + 2986, + 2987, + 2988, + 2989, + 2990, + 2991, + 2992, + 2993, + 2994, + 2995, + 2996, + 2997, + 2998, + 2999, + 3000, + 3001, + 3002, + 3003, + 3004, + 3005, + 3006, + 3007, + 3008, + 3009, + 3010, + 3011, + 3012, + 3013, + 3014, + 3015, + 3016, + 3017, + 3018, + 3019, + 3020, + 3021, + 3022, + 3023, + 3024, + 3025, + 3026, + 3027, + 3028, + 3029, + 3030, + 3031, + 3032, + 3033, + 3034, + 3035, + 3036, + 3037, + 3038, + 3039, + 3040, + 3041, + 3042, + 3043, + 3044, + 3045, + 3046, + 3047, + 3048, + 3049, + 3050, + 3051, + 3052, + 3053, + 3054, + 3055, + 3056, + 3057, + 3058, + 3059, + 3060, + 3061, + 3062, + 3063, + 3064, + 3065, + 3066, + 3067, + 3068, + 3069, + 3070, + 3071, + 3072, + 3073, + 3074, + 3075, + 3076, + 3077, + 3078, + 3079, + 3080, + 3081, + 3082, + 3083, + 3084, + 3085, + 3086, + 3087, + 3088, + 3089, + 3090, + 3091, + 3092, + 3093, + 3094, + 3095, + 3096, + 3097, + 3098, + 3099, + 3100, + 3101, + 3102, + 3103, + 3104, + 3105, + 3106, + 3107, + 3108, + 3109, + 3110, + 3111, + 3112, + 3113, + 3114, + 3115, + 3116, + 3117, + 3118, + 3119, + 3120, + 3121, + 3122, + 3123, + 3124, + 3125, + 3126, + 3127, + 3128, + 3129, + 3130, + 3131, + 3132, + 3133, + 3134, + 3135, + 3136, + 3137, + 3138, + 3139, + 3140, + 3141, + 3142, + 3143, + 3144, + 3145, + 3146, + 3147, + 3148, + 3149, + 3150, + 3151, + 3152, + 3153, + 3154, + 3155, + 3156, + 3157, + 3158, + 3159, + 3160, + 3161, + 3162, + 3163, + 3164, + 3165, + 3166, + 3167, + 3168, + 3169, + 3170, + 3171, + 3172, + 3173, + 3174, + 3175, + 3176, + 3177, + 3178, + 3179, + 3180, + 3181, + 3182, + 3183, + 3184, + 3185, + 3186, + 3187, + 3188, + 3189, + 3190, + 3191, + 3192, + 3193, + 3194, + 3195, + 3196, + 3197, + 3198, + 3199, + 3200, + 3201, + 3202, + 3203, + 3204, + 3205, + 3206, + 3207, + 3208, + 3209, + 3210, + 3211, + 3212, + 3213, + 3214, + 3215, + 3216, + 3217, + 3218, + 3219, + 3220, + 3221, + 3222, + 3223, + 3224, + 3225, + 3226, + 3227, + 3228, + 3229, + 3230, + 3231, + 3232, + 3233, + 3234, + 3235, + 3236, + 3237, + 3238, + 3239, + 3240, + 3241, + 3242, + 3243, + 3244, + 3245, + 3255, + 3260, + 3265, + 3270, + 3300 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Basic_Histogram.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Basic_Histogram.json new file mode 100644 index 0000000000000..a4fe5c1ca7f30 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Basic_Histogram.json @@ -0,0 +1,84 @@ +{ + "counts": [ + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 3, + 2, + 2, + 3, + 2, + 2, + 2 + ], + "values": [ + 11.5, + 13, + 14.5, + 16, + 17.5, + 19, + 20.5, + 22, + 23.5, + 25, + 27.5, + 30, + 32.5, + 35, + 37.5, + 40, + 42.5, + 45, + 47.5, + 50, + 52.5, + 55, + 57.5, + 60, + 62.5, + 65, + 67.5, + 70, + 72.5, + 75, + 77.5, + 80, + 82.5, + 85, + 87.5, + 107.14285714285714, + 114.28571428571429, + 121.42857142857143, + 200 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Cumulative_bucket_starts_at_0.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Cumulative_bucket_starts_at_0.json new file mode 100644 index 0000000000000..0abcfbc8f56aa --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Cumulative_bucket_starts_at_0.json @@ -0,0 +1,204 @@ +{ + "counts": [ + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 208, + 169, + 133, + 102, + 75, + 52, + 33, + 18, + 8, + 2, + 169, + 137, + 109, + 83, + 61, + 42, + 27, + 15, + 6, + 1, + 52, + 43, + 34, + 26, + 19, + 12, + 8, + 4, + 2, + 11, + 9, + 7, + 6, + 4, + 2, + 1, + 6, + 5, + 4, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 2, + 2, + 1, + 0, + 0, + 0, + 2, + 1 + ], + "values": [ + -0.0045000000000000005, + -0.004, + -0.0035, + -0.003, + -0.0025, + -0.002, + -0.0015, + -0.001, + -0.0004999999999999996, + 0, + 0.0005, + 0.001, + 0.0015, + 0.002, + 0.0025, + 0.003, + 0.0035, + 0.004, + 0.0045000000000000005, + 0.005, + 0.006500000000000001, + 0.008, + 0.009500000000000001, + 0.011, + 0.0125, + 0.014000000000000002, + 0.0155, + 0.017, + 0.0185, + 0.02, + 0.028, + 0.036000000000000004, + 0.044, + 0.052000000000000005, + 0.06, + 0.068, + 0.076, + 0.084, + 0.09200000000000001, + 0.1, + 0.11, + 0.12000000000000001, + 0.13, + 0.14, + 0.15000000000000002, + 0.16, + 0.17, + 0.18, + 0.19, + 0.2, + 0.23, + 0.26, + 0.29000000000000004, + 0.32, + 0.35, + 0.38, + 0.41000000000000003, + 0.44, + 0.47000000000000003, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.8500000000000001, + 0.9, + 0.95, + 1.1, + 1.2, + 1.3, + 1.4, + 1.5, + 1.6, + 1.7000000000000002, + 2.3, + 2.6, + 2.9, + 3.2, + 3.5, + 3.8, + 5.5, + 6, + 6.5, + 7, + 7.5, + 8, + 8.5, + 9, + 9.5, + 10, + 10.625, + 11.25, + 11.875, + 22.5, + 15 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/First_bucket_boundary_equals_minimum.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/First_bucket_boundary_equals_minimum.json new file mode 100644 index 0000000000000..c949e90515698 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/First_bucket_boundary_equals_minimum.json @@ -0,0 +1,94 @@ +{ + "counts": [ + 6, + 5, + 4, + 3, + 1, + 1, + 0, + 0, + 0, + 0, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 7, + 6, + 5, + 3, + 2, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 2, + 3, + 4, + 4, + 3, + 3, + 2, + 2 + ], + "values": [ + 9.1, + 9.2, + 9.3, + 9.4, + 9.5, + 9.6, + 9.7, + 9.8, + 9.9, + 10, + 16.5, + 23, + 29.5, + 36, + 42.5, + 49, + 55.5, + 62, + 68.5, + 75, + 77.5, + 80, + 82.5, + 85, + 87.5, + 90, + 92.5, + 95, + 97.5, + 100, + 105, + 110, + 115, + 120, + 125, + 130, + 135, + 140, + 145, + 150, + 151, + 152, + 153, + 160 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Large_Numbers.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Large_Numbers.json new file mode 100644 index 0000000000000..31a724ee234c1 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Large_Numbers.json @@ -0,0 +1,116 @@ +{ + "counts": [ + 53, + 43, + 34, + 26, + 18, + 13, + 8, + 4, + 2, + 79, + 64, + 51, + 38, + 28, + 19, + 12, + 7, + 3, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 24, + 39, + 32, + 25, + 20, + 15, + 9, + 6, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 13, + 11, + 9, + 7, + 4, + 3, + 2, + 1 + ], + "values": [ + 190000, + 280000, + 370000, + 460000, + 550000, + 640000, + 730000, + 820000, + 910000, + 1900000, + 2800000, + 3700000, + 4600000, + 5500000, + 6400000, + 7300000, + 8200000, + 9100000, + 14000000, + 18000000, + 22000000, + 26000000, + 30000000, + 34000000, + 38000000, + 42000000, + 46000000, + 50000000, + 55000000, + 60000000, + 65000000, + 70000000, + 75000000, + 80000000, + 85000000, + 90000000, + 95000000, + 140000000, + 180000000, + 220000000, + 260000000, + 300000000, + 340000000, + 380000000, + 420000000, + 460000000, + 500000000, + 550000000, + 600000000, + 650000000, + 700000000, + 750000000, + 800000000, + 850000000, + 1000000000 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Many_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Many_Buckets.json new file mode 100644 index 0000000000000..f32cbd17e8b81 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Many_Buckets.json @@ -0,0 +1,434 @@ +{ + "counts": [ + 14, + 11, + 9, + 7, + 4, + 3, + 2, + 1, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 14, + 11, + 9, + 7, + 4, + 3, + 2, + 1, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 14, + 12, + 9, + 7, + 5, + 3, + 2, + 1 + ], + "values": [ + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.8500000000000001, + 0.9, + 1.4, + 1.8, + 2.2, + 2.6, + 3, + 3.4000000000000004, + 3.8000000000000003, + 4.2, + 4.6, + 5, + 5.5, + 6, + 6.5, + 7, + 7.5, + 8, + 8.5, + 9, + 9.5, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 110, + 120, + 130, + 140, + 150, + 160, + 170, + 180, + 190, + 200, + 210, + 220, + 230, + 240, + 250, + 260, + 270, + 280, + 290, + 300, + 310, + 320, + 330, + 340, + 350, + 360, + 370, + 380, + 390, + 400, + 410, + 420, + 430, + 440, + 450, + 460, + 470, + 480, + 490, + 500, + 510, + 520, + 530, + 540, + 550, + 560, + 570, + 580, + 590, + 600, + 610, + 620, + 630, + 640, + 650, + 660, + 670, + 680, + 690, + 700, + 710, + 720, + 730, + 740, + 750, + 760, + 770, + 780, + 790, + 800, + 810, + 820, + 830, + 840, + 850, + 860, + 870, + 880, + 890, + 900, + 910, + 920, + 930, + 940, + 950, + 960, + 970, + 980, + 990, + 1000, + 1010, + 1020, + 1030, + 1040, + 1050, + 1060, + 1070, + 1100 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Negative_and_Positive_Boundaries.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Negative_and_Positive_Boundaries.json new file mode 100644 index 0000000000000..2ef3dd0f52635 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Negative_and_Positive_Boundaries.json @@ -0,0 +1,78 @@ +{ + "counts": [ + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 7, + 6, + 5, + 4, + 2, + 1, + 1, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 7, + 6, + 5, + 3, + 2, + 1, + 1 + ], + "values": [ + -48, + -46, + -44, + -42, + -40, + -38, + -36, + -34, + -32, + -30, + -28, + -26, + -24, + -22, + -20, + -18, + -16, + 6, + 10, + 12, + 14, + 16, + 18, + 20, + 22, + 24, + 26, + 28, + 30, + 32, + 34, + 36, + 38, + 40, + 42, + 50 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_Max_with_Single_Value.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_Max_with_Single_Value.json new file mode 100644 index 0000000000000..55b100a9420e5 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_Max_with_Single_Value.json @@ -0,0 +1,8 @@ +{ + "counts": [ + 1 + ], + "values": [ + 150 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_or_Max.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_or_Max.json new file mode 100644 index 0000000000000..abaea0a424368 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_or_Max.json @@ -0,0 +1,70 @@ +{ + "counts": [ + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 7, + 6, + 4, + 4, + 2, + 1, + 3, + 3, + 2, + 2, + 3, + 2 + ], + "values": [ + -26, + -22, + -18, + -14, + -10, + -6, + -2, + 2, + 6, + 10, + 14, + 18, + 22, + 26, + 30, + 34, + 38, + 42, + 46, + 50, + 55, + 60, + 65, + 70, + 75, + 80, + 110, + 120, + 130, + 140, + 180, + 160 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Max_Defined.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Max_Defined.json new file mode 100644 index 0000000000000..dbcc0f0206e63 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Max_Defined.json @@ -0,0 +1,90 @@ +{ + "counts": [ + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 3, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 2 + ], + "values": [ + 10, + 20, + 30, + 40, + 50, + 60, + 70, + 80, + 90, + 100, + 110, + 120, + 130, + 140, + 150, + 160, + 170, + 180, + 190, + 200, + 210, + 220, + 230, + 240, + 250, + 260, + 270, + 280, + 290, + 300, + 310, + 320, + 330, + 340, + 350, + 420, + 440, + 460, + 480, + 500, + 550, + 750 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Min_Defined.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Min_Defined.json new file mode 100644 index 0000000000000..eacb60ae33609 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Min_Defined.json @@ -0,0 +1,60 @@ +{ + "counts": [ + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 3, + 3, + 2, + 2, + 3, + 2 + ], + "values": [ + 27.5, + 30, + 32.5, + 35, + 37.5, + 40, + 42.5, + 45, + 47.5, + 50, + 55, + 60, + 65, + 70, + 75, + 80, + 85, + 90, + 95, + 100, + 105, + 110, + 115, + 120, + 125, + 140, + 130 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Negative_Boundaries.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Negative_Boundaries.json new file mode 100644 index 0000000000000..fb5f79be04224 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Negative_Boundaries.json @@ -0,0 +1,102 @@ +{ + "counts": [ + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2 + ], + "values": [ + -195, + -190, + -185, + -180, + -175, + -170, + -165, + -160, + -155, + -150, + -145, + -140, + -135, + -130, + -125, + -120, + -115, + -110, + -105, + -100, + -97.5, + -95, + -92.5, + -90, + -87.5, + -85, + -82.5, + -80, + -77.5, + -75, + -72.5, + -70, + -67.5, + -65, + -62.5, + -60, + -57.5, + -55, + -52.5, + -50, + -46.42857142857143, + -42.857142857142854, + -39.285714285714285, + -35.714285714285715, + -32.14285714285714, + -28.57142857142857, + -25, + -10 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Positive_boundaries_but_implied_Negative_Values.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Positive_boundaries_but_implied_Negative_Values.json new file mode 100644 index 0000000000000..ab61ba86ef1ad --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Positive_boundaries_but_implied_Negative_Values.json @@ -0,0 +1,102 @@ +{ + "counts": [ + 7, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 1, + 1 + ], + "values": [ + -90, + -80, + -70, + -60, + -50, + -40, + -30, + -20, + -10, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 32, + 34, + 36, + 38, + 40, + 42.5, + 45, + 60 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Single_Bucket.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Single_Bucket.json new file mode 100644 index 0000000000000..e33aad55dcba5 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Single_Bucket.json @@ -0,0 +1,8 @@ +{ + "counts": [ + 51 + ], + "values": [ + 40 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Tail_Heavy_Histogram.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Tail_Heavy_Histogram.json new file mode 100644 index 0000000000000..d0a395b791506 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Tail_Heavy_Histogram.json @@ -0,0 +1,128 @@ +{ + "counts": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 3, + 4, + 7, + 9, + 11, + 13, + 2, + 8, + 18, + 33, + 52, + 75, + 102, + 133, + 169, + 208, + 26, + 22, + 17, + 13, + 9, + 6, + 4, + 2, + 1, + 3, + 3, + 2, + 2 + ], + "values": [ + 15, + 20, + 25, + 28.571428571428573, + 32.142857142857146, + 35.714285714285715, + 39.285714285714285, + 42.85714285714286, + 46.42857142857143, + 50, + 52.5, + 55, + 57.5, + 60, + 62.5, + 65, + 67.5, + 70, + 72.5, + 75, + 77.5, + 80, + 82.5, + 85, + 87.5, + 90, + 92.5, + 95, + 97.5, + 100, + 107.5, + 110, + 112.5, + 115, + 117.5, + 120, + 122.5, + 125, + 127.5, + 130, + 132.5, + 135, + 137.5, + 140, + 142.5, + 145, + 147.5, + 150, + 150.1, + 150.2, + 150.3, + 150.4, + 150.5, + 150.6, + 150.7, + 150.8, + 150.9, + 155.9, + 160.8, + 165.7, + 151 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Two_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Two_Buckets.json new file mode 100644 index 0000000000000..492f791b5b53a --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Two_Buckets.json @@ -0,0 +1,34 @@ +{ + "counts": [ + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 2, + 2 + ], + "values": [ + 1.4, + 1.8, + 2.2, + 2.6, + 3, + 3.4000000000000004, + 3.8000000000000003, + 4.2, + 4.6, + 5, + 5.5, + 6, + 6.5, + 10 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Unbounded_Histogram.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Unbounded_Histogram.json new file mode 100644 index 0000000000000..fd58d611c3349 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Unbounded_Histogram.json @@ -0,0 +1,8 @@ +{ + "counts": [ + 75 + ], + "values": [ + 0 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Very_Small_Numbers.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Very_Small_Numbers.json new file mode 100644 index 0000000000000..3ad0c6d1cbaf2 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Very_Small_Numbers.json @@ -0,0 +1,92 @@ +{ + "counts": [ + 3, + 3, + 2, + 2, + 1, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 3, + 2, + 2, + 3, + 1, + 1 + ], + "values": [ + 1.8999999999999998e-8, + 2.8e-8, + 3.7e-8, + 4.6e-8, + 5.5e-8, + 1.8999999999999998e-7, + 2.7999999999999997e-7, + 3.7e-7, + 4.6e-7, + 5.5e-7, + 6.4e-7, + 7.3e-7, + 8.2e-7, + 9.1e-7, + 0.000001, + 0.0000011, + 0.0000012, + 0.0000012999999999999998, + 0.0000014, + 0.0000015, + 0.0000016, + 0.0000016999999999999998, + 0.0000018, + 0.0000019, + 0.000002, + 0.0000021, + 0.0000022, + 0.0000023, + 0.0000024, + 0.0000024999999999999998, + 0.0000026, + 0.0000027, + 0.0000028000000000000003, + 0.0000029, + 0.000003, + 0.0000031, + 0.0000032, + 0.0000032999999999999997, + 0.0000034, + 0.0000035, + 0.00000425, + 0.0000045, + 0.000006 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Zero_Counts_and_Sparse_Data.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Zero_Counts_and_Sparse_Data.json new file mode 100644 index 0000000000000..a10b64f9576bf --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/Zero_Counts_and_Sparse_Data.json @@ -0,0 +1,46 @@ +{ + "counts": [ + 14, + 11, + 9, + 7, + 4, + 3, + 2, + 1, + 11, + 9, + 7, + 5, + 4, + 2, + 1, + 3, + 3, + 2, + 2, + 1 + ], + "values": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 140, + 180, + 220, + 260, + 300, + 340, + 380, + 1050, + 1100, + 1150, + 1200, + 1500 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/lognormal.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/lognormal.json new file mode 100644 index 0000000000000..22483f866c273 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/lognormal.json @@ -0,0 +1,1058 @@ +{ + "counts": [ + 45, + 44, + 44, + 44, + 44, + 44, + 44, + 44, + 44, + 44, + 87, + 87, + 87, + 86, + 86, + 86, + 86, + 86, + 86, + 86, + 90, + 90, + 90, + 90, + 90, + 90, + 89, + 89, + 89, + 89, + 77, + 77, + 77, + 77, + 77, + 77, + 77, + 77, + 77, + 76, + 68, + 68, + 68, + 68, + 67, + 67, + 67, + 67, + 67, + 67, + 59, + 59, + 59, + 59, + 59, + 59, + 59, + 59, + 59, + 58, + 51, + 51, + 51, + 51, + 51, + 51, + 50, + 50, + 50, + 50, + 46, + 46, + 46, + 46, + 46, + 46, + 45, + 45, + 45, + 45, + 42, + 42, + 42, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 35, + 35, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 32, + 32, + 32, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 26, + 26, + 26, + 26, + 26, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 23, + 23, + 23, + 23, + 23, + 23, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 18, + 21, + 21, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 18, + 18, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 11, + 11, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 10, + 10, + 10, + 10, + 10, + 10, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 7, + 7, + 7, + 8, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 6, + 6, + 6, + 6, + 6, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 6, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 7, + 7, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 7, + 7, + 7, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 111, + 90, + 71, + 55, + 40, + 28, + 18, + 9, + 4, + 1, + 27, + 22, + 18, + 14, + 9, + 6, + 4, + 2, + 1, + 7, + 6, + 5, + 4, + 2, + 1, + 1 + ], + "values": [ + 0.000145693, + 0.000240616, + 0.000335539, + 0.000430462, + 0.000525385, + 0.000620308, + 0.000715231, + 0.000810154, + 0.000905077, + 0.001, + 0.0011, + 0.0012000000000000001, + 0.0013, + 0.0014, + 0.0015, + 0.0016, + 0.0017000000000000001, + 0.0018, + 0.0019000000000000002, + 0.002, + 0.0021, + 0.0022, + 0.0023, + 0.0024000000000000002, + 0.0025, + 0.0026, + 0.0027, + 0.0028, + 0.0029000000000000002, + 0.003, + 0.0031, + 0.0032, + 0.0033, + 0.0034000000000000002, + 0.0035, + 0.0036, + 0.0037, + 0.0038, + 0.0039000000000000003, + 0.004, + 0.0041, + 0.0042, + 0.0043, + 0.0044, + 0.0045000000000000005, + 0.0046, + 0.0047, + 0.0048000000000000004, + 0.0049, + 0.005, + 0.0051, + 0.0052, + 0.0053, + 0.0054, + 0.0055, + 0.0056, + 0.0057, + 0.0058000000000000005, + 0.0059, + 0.006, + 0.0061, + 0.0062, + 0.0063, + 0.0064, + 0.006500000000000001, + 0.0066, + 0.0067, + 0.0068000000000000005, + 0.0069, + 0.007, + 0.0071, + 0.0072, + 0.0073, + 0.0074, + 0.0075, + 0.0076, + 0.0077, + 0.0078000000000000005, + 0.0079, + 0.008, + 0.0081, + 0.0082, + 0.0083, + 0.0084, + 0.0085, + 0.0086, + 0.0087, + 0.008799999999999999, + 0.0089, + 0.009, + 0.009099999999999999, + 0.0092, + 0.0093, + 0.0094, + 0.0095, + 0.0096, + 0.0097, + 0.0098, + 0.0099, + 0.01, + 0.0101, + 0.0102, + 0.0103, + 0.0104, + 0.010499999999999999, + 0.0106, + 0.0107, + 0.010799999999999999, + 0.0109, + 0.011, + 0.011099999999999999, + 0.0112, + 0.0113, + 0.0114, + 0.0115, + 0.0116, + 0.0117, + 0.0118, + 0.0119, + 0.012, + 0.0121, + 0.0122, + 0.0123, + 0.0124, + 0.0125, + 0.0126, + 0.0127, + 0.012799999999999999, + 0.0129, + 0.013, + 0.013099999999999999, + 0.0132, + 0.0133, + 0.0134, + 0.0135, + 0.0136, + 0.0137, + 0.0138, + 0.013900000000000001, + 0.014, + 0.0141, + 0.0142, + 0.0143, + 0.0144, + 0.014499999999999999, + 0.0146, + 0.0147, + 0.014799999999999999, + 0.0149, + 0.015, + 0.015099999999999999, + 0.0152, + 0.0153, + 0.0154, + 0.0155, + 0.0156, + 0.0157, + 0.0158, + 0.0159, + 0.016, + 0.0161, + 0.0162, + 0.016300000000000002, + 0.0164, + 0.0165, + 0.0166, + 0.0167, + 0.016800000000000002, + 0.016900000000000002, + 0.017, + 0.0171, + 0.0172, + 0.0173, + 0.0174, + 0.0175, + 0.0176, + 0.0177, + 0.0178, + 0.0179, + 0.018, + 0.018099999999999998, + 0.018199999999999997, + 0.0183, + 0.0184, + 0.0185, + 0.0186, + 0.018699999999999998, + 0.0188, + 0.0189, + 0.019, + 0.0191, + 0.0192, + 0.0193, + 0.0194, + 0.0195, + 0.0196, + 0.0197, + 0.0198, + 0.0199, + 0.02, + 0.0201, + 0.0202, + 0.020300000000000002, + 0.0204, + 0.0205, + 0.0206, + 0.0207, + 0.020800000000000003, + 0.020900000000000002, + 0.021, + 0.0211, + 0.0212, + 0.0213, + 0.0214, + 0.0215, + 0.0216, + 0.0217, + 0.0218, + 0.0219, + 0.022, + 0.022099999999999998, + 0.022199999999999998, + 0.0223, + 0.0224, + 0.0225, + 0.0226, + 0.022699999999999998, + 0.0228, + 0.0229, + 0.023, + 0.0231, + 0.0232, + 0.0233, + 0.0234, + 0.0235, + 0.0236, + 0.0237, + 0.0238, + 0.0239, + 0.024, + 0.0241, + 0.0242, + 0.024300000000000002, + 0.0244, + 0.0245, + 0.0246, + 0.0247, + 0.024800000000000003, + 0.024900000000000002, + 0.025, + 0.0251, + 0.0252, + 0.0253, + 0.0254, + 0.025500000000000002, + 0.0256, + 0.0257, + 0.0258, + 0.0259, + 0.026, + 0.026099999999999998, + 0.026199999999999998, + 0.0263, + 0.0264, + 0.0265, + 0.0266, + 0.026699999999999998, + 0.0268, + 0.0269, + 0.027, + 0.0271, + 0.0272, + 0.0273, + 0.0274, + 0.0275, + 0.0276, + 0.0277, + 0.027800000000000002, + 0.0279, + 0.028, + 0.0281, + 0.0282, + 0.028300000000000002, + 0.0284, + 0.0285, + 0.0286, + 0.0287, + 0.028800000000000003, + 0.028900000000000002, + 0.029, + 0.0291, + 0.0292, + 0.0293, + 0.0294, + 0.0295, + 0.0296, + 0.0297, + 0.0298, + 0.0299, + 0.03, + 0.0301, + 0.030199999999999998, + 0.0303, + 0.0304, + 0.0305, + 0.0306, + 0.030699999999999998, + 0.0308, + 0.0309, + 0.031, + 0.0311, + 0.0312, + 0.0313, + 0.0314, + 0.0315, + 0.0316, + 0.0317, + 0.0318, + 0.0319, + 0.032, + 0.032100000000000004, + 0.0322, + 0.0323, + 0.0324, + 0.0325, + 0.032600000000000004, + 0.0327, + 0.0328, + 0.0329, + 0.033, + 0.033100000000000004, + 0.0332, + 0.0333, + 0.0334, + 0.0335, + 0.033600000000000005, + 0.0337, + 0.033800000000000004, + 0.0339, + 0.034, + 0.034100000000000005, + 0.0342, + 0.034300000000000004, + 0.0344, + 0.0345, + 0.034600000000000006, + 0.0347, + 0.034800000000000005, + 0.0349, + 0.035, + 0.035100000000000006, + 0.0352, + 0.0353, + 0.0354, + 0.035500000000000004, + 0.0356, + 0.035699999999999996, + 0.0358, + 0.0359, + 0.036, + 0.0361, + 0.036199999999999996, + 0.0363, + 0.036399999999999995, + 0.0365, + 0.0366, + 0.036699999999999997, + 0.0368, + 0.036899999999999995, + 0.037, + 0.0371, + 0.0372, + 0.0373, + 0.037399999999999996, + 0.0375, + 0.0376, + 0.0377, + 0.0378, + 0.037899999999999996, + 0.038, + 0.0381, + 0.0382, + 0.0383, + 0.0384, + 0.0385, + 0.0386, + 0.0387, + 0.0388, + 0.0389, + 0.039, + 0.0391, + 0.0392, + 0.0393, + 0.0394, + 0.0395, + 0.0396, + 0.0397, + 0.0398, + 0.0399, + 0.04, + 0.040100000000000004, + 0.0402, + 0.0403, + 0.0404, + 0.0405, + 0.040600000000000004, + 0.0407, + 0.0408, + 0.0409, + 0.041, + 0.041100000000000005, + 0.0412, + 0.0413, + 0.0414, + 0.0415, + 0.041600000000000005, + 0.0417, + 0.041800000000000004, + 0.0419, + 0.042, + 0.0421, + 0.0422, + 0.042300000000000004, + 0.0424, + 0.042499999999999996, + 0.0426, + 0.0427, + 0.0428, + 0.042899999999999994, + 0.043, + 0.0431, + 0.043199999999999995, + 0.0433, + 0.043399999999999994, + 0.0435, + 0.0436, + 0.043699999999999996, + 0.0438, + 0.043899999999999995, + 0.044, + 0.0441, + 0.044199999999999996, + 0.0443, + 0.044399999999999995, + 0.0445, + 0.0446, + 0.0447, + 0.0448, + 0.044899999999999995, + 0.045, + 0.0451, + 0.0452, + 0.0453, + 0.045399999999999996, + 0.0455, + 0.0456, + 0.0457, + 0.0458, + 0.045899999999999996, + 0.046, + 0.0461, + 0.0462, + 0.0463, + 0.0464, + 0.0465, + 0.0466, + 0.0467, + 0.0468, + 0.0469, + 0.047, + 0.0471, + 0.0472, + 0.0473, + 0.0474, + 0.0475, + 0.0476, + 0.0477, + 0.0478, + 0.0479, + 0.048, + 0.048100000000000004, + 0.0482, + 0.0483, + 0.0484, + 0.0485, + 0.048600000000000004, + 0.0487, + 0.0488, + 0.0489, + 0.049, + 0.049100000000000005, + 0.0492, + 0.049300000000000004, + 0.0494, + 0.0495, + 0.049600000000000005, + 0.0497, + 0.049800000000000004, + 0.0499, + 0.05, + 0.055, + 0.060000000000000005, + 0.065, + 0.07, + 0.07500000000000001, + 0.08, + 0.085, + 0.09, + 0.095, + 0.1, + 0.11, + 0.12000000000000001, + 0.13, + 0.14, + 0.15000000000000002, + 0.16, + 0.17, + 0.18, + 0.19, + 0.24374, + 0.28748, + 0.33121999999999996, + 0.37495999999999996, + 0.41869999999999996, + 0.46243999999999996, + 0.6374 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/weibull.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/weibull.json new file mode 100644 index 0000000000000..fa323327e1f39 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/exponential/weibull.json @@ -0,0 +1,1024 @@ +{ + "counts": [ + 81, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 73, + 73, + 73, + 73, + 73, + 73, + 73, + 73, + 73, + 73, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 65, + 65, + 59, + 59, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 53, + 53, + 53, + 53, + 52, + 52, + 52, + 52, + 52, + 52, + 47, + 47, + 47, + 47, + 46, + 46, + 46, + 46, + 46, + 46, + 43, + 43, + 43, + 43, + 43, + 42, + 42, + 42, + 42, + 42, + 46, + 46, + 46, + 46, + 46, + 46, + 46, + 45, + 45, + 45, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 37, + 37, + 37, + 37, + 36, + 36, + 36, + 36, + 36, + 36, + 31, + 31, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 31, + 31, + 31, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 28, + 25, + 25, + 25, + 25, + 25, + 25, + 24, + 24, + 24, + 24, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 23, + 23, + 23, + 23, + 23, + 23, + 19, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 22, + 22, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 16, + 16, + 16, + 16, + 16, + 16, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 14, + 14, + 14, + 14, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 13, + 13, + 13, + 13, + 13, + 13, + 12, + 12, + 12, + 12, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 12, + 13, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 9, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 7, + 7, + 10, + 10, + 10, + 10, + 10, + 9, + 9, + 9, + 9, + 9, + 7, + 7, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 6, + 6, + 6, + 7, + 7, + 7, + 7, + 7, + 6, + 6, + 6, + 6, + 6, + 8, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 6, + 6, + 6, + 6, + 6, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 5, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 6, + 5, + 4, + 3, + 3, + 2, + 65, + 53, + 42, + 32, + 24, + 16, + 10, + 5, + 2, + 3, + 3, + 2, + 2 + ], + "values": [ + 0.00010002863800000001, + 0.00020002545600000002, + 0.000300022274, + 0.000400019092, + 0.0005000159100000001, + 0.000600012728, + 0.0007000095460000001, + 0.0008000063640000001, + 0.0009000031820000001, + 0.001, + 0.0011, + 0.0012000000000000001, + 0.0013, + 0.0014, + 0.0015, + 0.0016, + 0.0017000000000000001, + 0.0018, + 0.0019000000000000002, + 0.002, + 0.0021, + 0.0022, + 0.0023, + 0.0024000000000000002, + 0.0025, + 0.0026, + 0.0027, + 0.0028, + 0.0029000000000000002, + 0.003, + 0.0031, + 0.0032, + 0.0033, + 0.0034000000000000002, + 0.0035, + 0.0036, + 0.0037, + 0.0038, + 0.0039000000000000003, + 0.004, + 0.0041, + 0.0042, + 0.0043, + 0.0044, + 0.0045000000000000005, + 0.0046, + 0.0047, + 0.0048000000000000004, + 0.0049, + 0.005, + 0.0051, + 0.0052, + 0.0053, + 0.0054, + 0.0055, + 0.0056, + 0.0057, + 0.0058000000000000005, + 0.0059, + 0.006, + 0.0061, + 0.0062, + 0.0063, + 0.0064, + 0.006500000000000001, + 0.0066, + 0.0067, + 0.0068000000000000005, + 0.0069, + 0.007, + 0.0071, + 0.0072, + 0.0073, + 0.0074, + 0.0075, + 0.0076, + 0.0077, + 0.0078000000000000005, + 0.0079, + 0.008, + 0.0081, + 0.0082, + 0.0083, + 0.0084, + 0.0085, + 0.0086, + 0.0087, + 0.008799999999999999, + 0.0089, + 0.009, + 0.009099999999999999, + 0.0092, + 0.0093, + 0.0094, + 0.0095, + 0.0096, + 0.0097, + 0.0098, + 0.0099, + 0.01, + 0.0101, + 0.0102, + 0.0103, + 0.0104, + 0.010499999999999999, + 0.0106, + 0.0107, + 0.010799999999999999, + 0.0109, + 0.011, + 0.011099999999999999, + 0.0112, + 0.0113, + 0.0114, + 0.0115, + 0.0116, + 0.0117, + 0.0118, + 0.0119, + 0.012, + 0.0121, + 0.0122, + 0.0123, + 0.0124, + 0.0125, + 0.0126, + 0.0127, + 0.012799999999999999, + 0.0129, + 0.013, + 0.013099999999999999, + 0.0132, + 0.0133, + 0.0134, + 0.0135, + 0.0136, + 0.0137, + 0.0138, + 0.013900000000000001, + 0.014, + 0.0141, + 0.0142, + 0.0143, + 0.0144, + 0.014499999999999999, + 0.0146, + 0.0147, + 0.014799999999999999, + 0.0149, + 0.015, + 0.015099999999999999, + 0.0152, + 0.0153, + 0.0154, + 0.0155, + 0.0156, + 0.0157, + 0.0158, + 0.0159, + 0.016, + 0.0161, + 0.0162, + 0.016300000000000002, + 0.0164, + 0.0165, + 0.0166, + 0.0167, + 0.016800000000000002, + 0.016900000000000002, + 0.017, + 0.0171, + 0.0172, + 0.0173, + 0.0174, + 0.0175, + 0.0176, + 0.0177, + 0.0178, + 0.0179, + 0.018, + 0.018099999999999998, + 0.018199999999999997, + 0.0183, + 0.0184, + 0.0185, + 0.0186, + 0.018699999999999998, + 0.0188, + 0.0189, + 0.019, + 0.0191, + 0.0192, + 0.0193, + 0.0194, + 0.0195, + 0.0196, + 0.0197, + 0.0198, + 0.0199, + 0.02, + 0.0201, + 0.0202, + 0.020300000000000002, + 0.0204, + 0.0205, + 0.0206, + 0.0207, + 0.020800000000000003, + 0.020900000000000002, + 0.021, + 0.0211, + 0.0212, + 0.0213, + 0.0214, + 0.0215, + 0.0216, + 0.0217, + 0.0218, + 0.0219, + 0.022, + 0.022099999999999998, + 0.022199999999999998, + 0.0223, + 0.0224, + 0.0225, + 0.0226, + 0.022699999999999998, + 0.0228, + 0.0229, + 0.023, + 0.0231, + 0.0232, + 0.0233, + 0.0234, + 0.0235, + 0.0236, + 0.0237, + 0.0238, + 0.0239, + 0.024, + 0.0241, + 0.0242, + 0.024300000000000002, + 0.0244, + 0.0245, + 0.0246, + 0.0247, + 0.024800000000000003, + 0.024900000000000002, + 0.025, + 0.0251, + 0.0252, + 0.0253, + 0.0254, + 0.025500000000000002, + 0.0256, + 0.0257, + 0.0258, + 0.0259, + 0.026, + 0.026099999999999998, + 0.026199999999999998, + 0.0263, + 0.0264, + 0.0265, + 0.0266, + 0.026699999999999998, + 0.0268, + 0.0269, + 0.027, + 0.0271, + 0.0272, + 0.0273, + 0.0274, + 0.0275, + 0.0276, + 0.0277, + 0.027800000000000002, + 0.0279, + 0.028, + 0.0281, + 0.0282, + 0.028300000000000002, + 0.0284, + 0.0285, + 0.0286, + 0.0287, + 0.028800000000000003, + 0.028900000000000002, + 0.029, + 0.0291, + 0.0292, + 0.0293, + 0.0294, + 0.0295, + 0.0296, + 0.0297, + 0.0298, + 0.0299, + 0.03, + 0.0301, + 0.030199999999999998, + 0.0303, + 0.0304, + 0.0305, + 0.0306, + 0.030699999999999998, + 0.0308, + 0.0309, + 0.031, + 0.0311, + 0.0312, + 0.0313, + 0.0314, + 0.0315, + 0.0316, + 0.0317, + 0.0318, + 0.0319, + 0.032, + 0.032100000000000004, + 0.0322, + 0.0323, + 0.0324, + 0.0325, + 0.032600000000000004, + 0.0327, + 0.0328, + 0.0329, + 0.033, + 0.033100000000000004, + 0.0332, + 0.0333, + 0.0334, + 0.0335, + 0.033600000000000005, + 0.0337, + 0.033800000000000004, + 0.0339, + 0.034, + 0.034100000000000005, + 0.0342, + 0.034300000000000004, + 0.0344, + 0.0345, + 0.034600000000000006, + 0.0347, + 0.034800000000000005, + 0.0349, + 0.035, + 0.035100000000000006, + 0.0352, + 0.0353, + 0.0354, + 0.035500000000000004, + 0.0356, + 0.035699999999999996, + 0.0358, + 0.0359, + 0.036, + 0.0361, + 0.036199999999999996, + 0.0363, + 0.036399999999999995, + 0.0365, + 0.0366, + 0.036699999999999997, + 0.0368, + 0.036899999999999995, + 0.037, + 0.0371, + 0.0372, + 0.0373, + 0.037399999999999996, + 0.0375, + 0.0376, + 0.0377, + 0.0378, + 0.037899999999999996, + 0.038, + 0.0381, + 0.0382, + 0.0383, + 0.0384, + 0.0385, + 0.0386, + 0.0387, + 0.0388, + 0.0389, + 0.039, + 0.0391, + 0.0392, + 0.0393, + 0.0394, + 0.0395, + 0.0396, + 0.0397, + 0.0398, + 0.0399, + 0.04, + 0.040100000000000004, + 0.0402, + 0.0403, + 0.0404, + 0.0405, + 0.040600000000000004, + 0.0407, + 0.0408, + 0.0409, + 0.041, + 0.041100000000000005, + 0.0412, + 0.0413, + 0.0414, + 0.0415, + 0.041600000000000005, + 0.0417, + 0.041800000000000004, + 0.0419, + 0.042, + 0.0421, + 0.0422, + 0.042300000000000004, + 0.0424, + 0.042499999999999996, + 0.0426, + 0.0427, + 0.0428, + 0.042899999999999994, + 0.043, + 0.0431, + 0.043199999999999995, + 0.0433, + 0.043399999999999994, + 0.0435, + 0.0436, + 0.043699999999999996, + 0.0438, + 0.043899999999999995, + 0.044, + 0.0441, + 0.044199999999999996, + 0.0443, + 0.044399999999999995, + 0.0445, + 0.0446, + 0.0447, + 0.0448, + 0.044899999999999995, + 0.045, + 0.0451, + 0.0452, + 0.0453, + 0.045399999999999996, + 0.0455, + 0.0456, + 0.0457, + 0.0458, + 0.045899999999999996, + 0.046, + 0.0461, + 0.0462, + 0.0463, + 0.0464, + 0.0465, + 0.0466, + 0.0467, + 0.0468, + 0.0469, + 0.047, + 0.0471, + 0.0472, + 0.0473, + 0.0474, + 0.0475, + 0.0476, + 0.0477, + 0.0478, + 0.0479, + 0.048, + 0.048100000000000004, + 0.0482, + 0.0483, + 0.0484, + 0.0485, + 0.048600000000000004, + 0.0487, + 0.0488, + 0.0489, + 0.049, + 0.049100000000000005, + 0.0492, + 0.049300000000000004, + 0.0494, + 0.0495, + 0.049600000000000005, + 0.055, + 0.060000000000000005, + 0.065, + 0.07, + 0.07500000000000001, + 0.08, + 0.085, + 0.09, + 0.095, + 0.11, + 0.12000000000000001, + 0.13, + 0.1332 + ] +} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py b/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py new file mode 100644 index 0000000000000..b507d34458948 --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py @@ -0,0 +1,166 @@ +import argparse +import json +import math +import matplotlib.pyplot as plt +import numpy as np +import os +import pdb + +from pathlib import Path +from typing import Dict, List, Tuple + +def plot_original_histogram(data, ax, title: str, color: str): + """Plot original histogram using exact bucket boundaries.""" + boundaries = data.get('Boundaries', []) + counts = data['Counts'] + min_val = data.get('Min') + max_val = data.get('Max') + total_count = sum(counts) + + # Handle case with no boundaries (single bucket) + if not boundaries or len(boundaries) == 0: + if min_val is not None and max_val is not None: + left_edges = [min_val] + widths = [max_val - min_val] + else: + # Use arbitrary range if no min/max + left_edges = [-10] + widths = [20] + else: + # Calculate exact bucket edges and widths + left_edges = [] + widths = [] + + for i in range(len(counts)): + if i == 0: + # First bucket: from min to first boundary + left = min_val if min_val is not None else boundaries[0] - (boundaries[1] - boundaries[0]) if len(boundaries) > 1 else boundaries[0] - 10 + right = boundaries[0] + elif i == len(counts) - 1: + # Last bucket: from last boundary to max + left = boundaries[i-1] + right = max_val if max_val is not None else boundaries[i-1] + (boundaries[i-1] - boundaries[i-2]) if len(boundaries) > 1 else boundaries[i-1] + 10 + else: + # Middle buckets: between boundaries + left = boundaries[i-1] + right = boundaries[i] + + left_edges.append(left) + widths.append(right - left) + + ax.bar(left_edges, counts, width=widths, alpha=0.7, edgecolor='black', linewidth=0.8, color=color, align='edge') + ax.set_title(f'{title} (Count: {total_count})') + ax.set_ylabel('Counts') + ax.grid(True, alpha=0.3) + +def plot_cw_histogram_bars(histogram: Dict[float, float], histogram_min: float, histogram_max: float, ax, title: str, color: str): + """Plot histogram bars on given axes.""" + values = sorted(histogram.keys()) + counts = [histogram[v] for v in values] + total_count = sum(counts) + + if len(values) == 1: + # Single bar case + width = (histogram_max - histogram_min) * 0.8 + ax.bar(values, counts, width=width, alpha=0.7, edgecolor='black', linewidth=1.5, color=color) + else: + # Calculate minimum gap to prevent overlaps + gaps = [values[i+1] - values[i] for i in range(len(values)-1)] + min_gap = min(gaps) + max_width = min_gap * 0.8 # Use 80% of minimum gap + + widths = [] + for i in range(len(values)): + if i == 0: + # First bar: extend to histogram_min or use half-gap to next + left_space = values[0] - histogram_min + right_space = (values[1] - values[0]) / 2 if len(values) > 1 else (histogram_max - values[0]) + width = min(left_space + right_space, max_width) + elif i == len(values) - 1: + # Last bar: extend to histogram_max or use half-gap from previous + left_space = (values[i] - values[i-1]) / 2 + right_space = histogram_max - values[i] + width = min(left_space + right_space, max_width) + else: + # Middle bars: use half-gaps on both sides + left_space = (values[i] - values[i-1]) / 2 + right_space = (values[i+1] - values[i]) / 2 + width = min(left_space + right_space, max_width) + + widths.append(width) + + ax.bar(values, counts, width=widths, alpha=0.7, edgecolor='black', linewidth=0.8, color=color) + + ax.scatter(values, counts, color='red', s=50, zorder=5) + ax.set_title(f'{title} (Count: {total_count})') + ax.set_ylabel('Counts') + ax.grid(True, alpha=0.3) + +def load_json_data(filepath): + """Load histogram data from JSON file.""" + with open(filepath, 'r') as f: + data = json.load(f) + return data['values'], data['counts'] + +def load_original_histogram(filepath): + """Load original histogram format.""" + with open(filepath, 'r') as f: + data = json.load(f) + return data + +def plot_all_folders_comparison(json_filename): + """Plot the same JSON file from all folders for comparison.""" + base_path = Path('.') + folders = ['original', 'cwagent', 'even', 'middlepoint', 'exponential', 'exponentialcw'] + colors = ['black', 'green', 'orange', 'red', 'purple', 'blue'] + + fig, ax = plt.subplots(len(folders), 1, figsize=(12, 20)) + + i = -1 + for folder, color in zip(folders, colors): + i += 1 + filepath = base_path / folder / (json_filename+".json") + if filepath.exists(): + try: + if folder == 'original': + data = load_original_histogram(filepath) + plot_original_histogram(data, ax[i], f'{folder.capitalize()} Mapping', color) + else: + values, counts = load_json_data(filepath) + if not values: # Skip if no values + continue + hist = {values[j]: counts[j] for j in range(len(values))} + plot_cw_histogram_bars(hist, min(values), max(values), ax[i], f'{folder.capitalize()} Mapping', color) + except Exception as e: + print(f"Error processing {filepath}: {e}") + + plt.tight_layout() + plt.savefig(f"comparisons/{json_filename}.png", dpi=300, bbox_inches='tight') + plt.show() + +# Example usage +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Process histogram mappings') + parser.add_argument('dataset', nargs='?', help='Optional dataset name to process') + args = parser.parse_args() + + original_path = Path('./original') + if original_path.exists(): + if args.dataset: + # Process specific dataset if provided + dataset_file = original_path / f"{args.dataset}.json" + if dataset_file.exists(): + print(f"Processing {args.dataset}...") + plot_all_folders_comparison(args.dataset) + else: + print(f"Dataset '{args.dataset}' not found in original folder.") + else: + # Process all datasets if no specific dataset provided + json_files = [f.stem for f in original_path.iterdir() if f.suffix == '.json'] + for json_file in json_files: + print(f"Processing {json_file}...") + plot_all_folders_comparison(json_file) + else: + print("Original folder not found.") + + diff --git a/pkg/aws/cloudwatch/tool/cloudwatch_pusher.go b/pkg/aws/cloudwatch/tool/cloudwatch_pusher.go new file mode 100644 index 0000000000000..198678027e937 --- /dev/null +++ b/pkg/aws/cloudwatch/tool/cloudwatch_pusher.go @@ -0,0 +1,183 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package aws + +import ( + "context" + "encoding/json" + "fmt" + "io/fs" + "os" + "path/filepath" + "sort" + "strings" + "text/tabwriter" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" +) + +type DistributionData struct { + Values []float64 `json:"values"` + Counts []float64 `json:"counts"` +} + +type MetricInfo struct { + TestCase string + MappingType string + MetricName string +} + +func main() { + ctx := context.Background() + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + panic(err) + } + + cw := cloudwatch.NewFromConfig(cfg) + testDataDir := "testdata" + mappingTypes := []string{"cwagent", "exponential", "exponentialcw", "middlepoint", "even"} + + var metrics []MetricInfo + + // Push data to CloudWatch + for _, mappingType := range mappingTypes { + dirPath := filepath.Join(testDataDir, mappingType) + err := filepath.WalkDir(dirPath, func(path string, d fs.DirEntry, err error) error { + + if err != nil || !strings.HasSuffix(path, ".json") { + return err + } + + fileName := strings.TrimSuffix(filepath.Base(path), ".json") + metricName := fmt.Sprintf("HistogramDistribution_%s", mappingType) + metrics = append(metrics, MetricInfo{fileName, mappingType, metricName}) + //return nil + if err := pushDistributionToCloudWatch(ctx, cw, path, mappingType); err != nil { + fmt.Printf("Error pushing %s: %v\n", path, err) + } + return nil + }) + + if err != nil { + fmt.Printf("Error processing %s: %v\n", mappingType, err) + } + } + + // Wait for metrics to be available + fmt.Println("Waiting for metrics to be available...") + time.Sleep(30 * time.Second) + + // Query percentiles and display table + queryPercentilesAndDisplayTable(ctx, cw, metrics) +} + +func pushDistributionToCloudWatch(ctx context.Context, cw *cloudwatch.Client, filePath, mappingType string) error { + data, err := os.ReadFile(filePath) + if err != nil { + return err + } + + var dist DistributionData + if err := json.Unmarshal(data, &dist); err != nil { + return err + } + + fileName := strings.TrimSuffix(filepath.Base(filePath), ".json") + metricName := fmt.Sprintf("HistogramDistribution_%s", mappingType) + + // Convert to CloudWatch distribution format + values := make([]float64, len(dist.Values)) + counts := make([]float64, len(dist.Counts)) + copy(values, dist.Values) + copy(counts, dist.Counts) + + _, err = cw.PutMetricData(ctx, &cloudwatch.PutMetricDataInput{ + Namespace: aws.String("HistogramTesting"), + MetricData: []types.MetricDatum{ + { + MetricName: aws.String(metricName), + Dimensions: []types.Dimension{ + { + Name: aws.String("TestCase"), + Value: aws.String(fileName), + }, + { + Name: aws.String("MappingType"), + Value: aws.String(mappingType), + }, + }, + Timestamp: aws.Time(time.Now()), + Values: values, + Counts: counts, + }, + }, + }) + + if err != nil { + return fmt.Errorf("failed to push %s: %w", filePath, err) + } + + fmt.Printf("Pushed %s (%s)\n", fileName, mappingType) + return nil +} + +func queryPercentilesAndDisplayTable(ctx context.Context, cw *cloudwatch.Client, metrics []MetricInfo) { + percentiles := []string{"10", "25", "50", "75", "90", "99", "99.9"} + endTime := time.Now() + startTime := endTime.Add(-1 * time.Minute) + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + fmt.Fprintln(w, "TestCase\tMappingType\tP10\tP25\tP50\tP75\tP90\tP99\tP99.9") + + // Group by test case for better organization + testCases := make(map[string][]MetricInfo) + for _, metric := range metrics { + testCases[metric.TestCase] = append(testCases[metric.TestCase], metric) + } + + // Sort test cases for consistent output + var sortedTestCases []string + for testCase := range testCases { + sortedTestCases = append(sortedTestCases, testCase) + } + sort.Strings(sortedTestCases) + + for _, testCase := range sortedTestCases { + for _, metric := range testCases[testCase] { + values := make([]string, len(percentiles)) + for i, p := range percentiles { + stat := fmt.Sprintf("p%s", p) + resp, err := cw.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{ + Namespace: aws.String("HistogramTesting"), + MetricName: aws.String(metric.MetricName), + Dimensions: []types.Dimension{ + {Name: aws.String("TestCase"), Value: aws.String(metric.TestCase)}, + {Name: aws.String("MappingType"), Value: aws.String(metric.MappingType)}, + }, + StartTime: aws.Time(startTime), + EndTime: aws.Time(endTime), + Period: aws.Int32(300), + ExtendedStatistics: []string{stat}, + }) + if err != nil { + fmt.Printf("Error querying %s: %v\n", metric.MetricName, err) + } + if err != nil || len(resp.Datapoints) == 0 { + values[i] = "N/A" + } else { + values[i] = fmt.Sprintf("%.5f", resp.Datapoints[0].ExtendedStatistics[stat]) + } + } + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", + metric.TestCase, metric.MappingType, + values[0], values[1], values[2], values[3], values[4], values[5], values[6]) + } + } + w.Flush() +} diff --git a/pkg/aws/cloudwatch/tool/go.mod b/pkg/aws/cloudwatch/tool/go.mod new file mode 100644 index 0000000000000..2e6d33f258601 --- /dev/null +++ b/pkg/aws/cloudwatch/tool/go.mod @@ -0,0 +1,23 @@ +module cloudwtach_pusher + +go 1.25.0 + +require ( + github.com/aws/aws-sdk-go-v2 v1.39.2 + github.com/aws/aws-sdk-go-v2/config v1.31.12 + github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.51.1 +) + +require ( + github.com/aws/aws-sdk-go-v2/credentials v1.18.16 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 // indirect + github.com/aws/smithy-go v1.23.0 // indirect +) diff --git a/pkg/aws/cloudwatch/tool/go.sum b/pkg/aws/cloudwatch/tool/go.sum new file mode 100644 index 0000000000000..4c421a84a4ac1 --- /dev/null +++ b/pkg/aws/cloudwatch/tool/go.sum @@ -0,0 +1,28 @@ +github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= +github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= +github.com/aws/aws-sdk-go-v2/config v1.31.12 h1:pYM1Qgy0dKZLHX2cXslNacbcEFMkDMl+Bcj5ROuS6p8= +github.com/aws/aws-sdk-go-v2/config v1.31.12/go.mod h1:/MM0dyD7KSDPR+39p9ZNVKaHDLb9qnfDurvVS2KAhN8= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 h1:Mv4Bc0mWmv6oDuSWTKnk+wgeqPL5DRFu5bQL9BGPQ8Y= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9/go.mod h1:IKlKfRppK2a1y0gy1yH6zD+yX5uplJ6UuPlgd48dJiQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.51.1 h1:GqVafesryYki8Lw/yRzLcoSeaT06qSAIbLoZLqeY0ks= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.51.1/go.mod h1:Kg/y+WTU5U8KtZ8vYYz0CyiR8UCBbZkpsT7TeqIkQ2M= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 h1:5r34CgVOD4WZudeEKZ9/iKpiT6cM1JyEROpXjOcdWv8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9/go.mod h1:dB12CEbNWPbzO2uC6QSWHteqOg4JfBVJOojbAoAUb5I= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 h1:A1oRkiSQOWstGh61y4Wc/yQ04sqrQZr1Si/oAXj20/s= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.6/go.mod h1:5PfYspyCU5Vw1wNPsxi15LZovOnULudOQuVxphSflQA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 h1:5fm5RTONng73/QA73LhCNR7UT9RpFH3hR6HWL6bIgVY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1/go.mod h1:xBEjWD13h+6nq+z4AkqSfSvqRKFgDIQeaMguAJndOWo= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47hZb5HUQ0tn6Q9kA= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= +github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= +github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= diff --git a/pkg/aws/tools/generator.go b/pkg/aws/tools/generator.go index ff5e44c78389f..698e109d936dc 100644 --- a/pkg/aws/tools/generator.go +++ b/pkg/aws/tools/generator.go @@ -86,20 +86,6 @@ func generateSamples(config DistributionConfig, rng *rand.Rand) []float64 { return data } -// gammaRandom generates a random sample from a gamma distribution with the given shape parameter. -// It uses the Marsaglia and Tsang method (2000) for efficient gamma random number generation. -// -// For shape < 1, it uses the transformation property: if X ~ Gamma(shape+1, 1), then -// X * U^(1/shape) ~ Gamma(shape, 1) where U ~ Uniform(0,1). -// -// For shape >= 1, it uses the squeeze acceptance method which is highly efficient -// with an acceptance rate > 95% for most shape values. -// -// Parameters: -// - shape: the shape parameter (α) of the gamma distribution, must be > 0 -// - rng: random number generator for sampling -// -// Returns: a random sample from Gamma(shape, 1) distribution func gammaRandom(shape float64, rng *rand.Rand) float64 { if shape < 1 { return gammaRandom(shape+1, rng) * math.Pow(rng.Float64(), 1/shape) From 4f495d2ee1ef9badd9526fd46957373d1d943034 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Wed, 15 Oct 2025 12:06:36 -0400 Subject: [PATCH 11/23] keep histogram values pos if all boundaries are pos --- pkg/aws/cloudwatch/histograms/conversion.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/aws/cloudwatch/histograms/conversion.go b/pkg/aws/cloudwatch/histograms/conversion.go index dc64f9023a8f5..4e23993568962 100644 --- a/pkg/aws/cloudwatch/histograms/conversion.go +++ b/pkg/aws/cloudwatch/histograms/conversion.go @@ -73,7 +73,15 @@ func ConvertOTelToCloudWatch(dp pmetric.HistogramDataPoint) cloudwatch.Histogram if lenBounds > 1 { bucketWidth = bounds.At(1) - bounds.At(0) } + em.minimum = bounds.At(0) - bucketWidth + + // if all boundaries are positive, assume all data is positive. this covers use cases where Prometheus histogram + // metric for non-zero values like request durations have their first bucket start at 0. for these metrics, + // a negative minimum will cause percentile metrics to be unavailable + if bounds.At(0) >= 0 { + em.minimum = max(em.minimum, 0.0) + } } if !dp.HasMax() { From 33035ab35899a680525e6df1a3fba724d822c49d Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Wed, 15 Oct 2025 12:14:22 -0400 Subject: [PATCH 12/23] fix max calculation when not provided --- pkg/aws/cloudwatch/histograms/conversion.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/aws/cloudwatch/histograms/conversion.go b/pkg/aws/cloudwatch/histograms/conversion.go index 4e23993568962..5e7dd389c28c4 100644 --- a/pkg/aws/cloudwatch/histograms/conversion.go +++ b/pkg/aws/cloudwatch/histograms/conversion.go @@ -87,7 +87,7 @@ func ConvertOTelToCloudWatch(dp pmetric.HistogramDataPoint) cloudwatch.Histogram if !dp.HasMax() { bucketWidth := 0.01 // arbitrary width - there's no information about this histogram to make an inference with if lenBounds > 1 { - bucketWidth = bounds.At(lenBounds-2) - bounds.At(lenBounds-1) + bucketWidth = bounds.At(lenBounds-1) - bounds.At(lenBounds-2) } em.maximum = bounds.At(lenBounds-1) + bucketWidth } From 33d5ecf5a0b3aefac0bc057a02eaf96547534c04 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Wed, 15 Oct 2025 12:23:21 -0400 Subject: [PATCH 13/23] Use buckets with data for inferring min/max --- pkg/aws/cloudwatch/histograms/conversion.go | 59 ++++++++++++++++----- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/pkg/aws/cloudwatch/histograms/conversion.go b/pkg/aws/cloudwatch/histograms/conversion.go index 5e7dd389c28c4..2dfb1988592c3 100644 --- a/pkg/aws/cloudwatch/histograms/conversion.go +++ b/pkg/aws/cloudwatch/histograms/conversion.go @@ -54,7 +54,7 @@ func ConvertOTelToCloudWatch(dp pmetric.HistogramDataPoint) cloudwatch.Histogram if lenBounds == 0 { em.counts = append(em.counts, float64(bucketCounts.At(0))) // recall that len(bucketCounts) = len(bounds)+1 if dp.HasMax() && dp.HasMin() { - em.values = append(em.values, em.minimum/2.0+em.maximum/2.0) // overflow safe average calculation + em.values = append(em.values, em.minimum/2.0+em.maximum/2.0) } else if dp.HasMax() { em.values = append(em.values, em.maximum) // only data point we have is the maximum } else if dp.HasMin() { @@ -69,27 +69,58 @@ func ConvertOTelToCloudWatch(dp pmetric.HistogramDataPoint) cloudwatch.Histogram // min and max and their lower and upper bounds respectively. The min and max are optional on the OTel datapoint. // When min and max are not defined, make some reasonable about about what the min/max could be if !dp.HasMin() { - bucketWidth := 0.001 // arbitrary width - there's no information about this histogram to make an inference with - if lenBounds > 1 { - bucketWidth = bounds.At(1) - bounds.At(0) + + // Find the first bucket which contains some data points. The min must be in that bucket + minBucketIdx := 0 + for i := 0; i < lenBucketCounts; i++ { + if bucketCounts.At(i) > 0 { + minBucketIdx = i + break + } } - em.minimum = bounds.At(0) - bucketWidth + // take the lower bound of the bucket. lower bound of bucket index n is boundary index n-1 + if minBucketIdx != 0 { + em.minimum = bounds.At(minBucketIdx - 1) + } else { + bucketWidth := 0.001 // arbitrary width - there's no information about this histogram to make an inference with if there are no bounds + if lenBounds > 1 { + bucketWidth = bounds.At(1) - bounds.At(0) + } + em.minimum = bounds.At(0) - bucketWidth - // if all boundaries are positive, assume all data is positive. this covers use cases where Prometheus histogram - // metric for non-zero values like request durations have their first bucket start at 0. for these metrics, - // a negative minimum will cause percentile metrics to be unavailable - if bounds.At(0) >= 0 { - em.minimum = max(em.minimum, 0.0) + // if all boundaries are positive, assume all data is positive. this covers use cases where Prometheus + // histogram metrics for non-zero values like request durations have their first bucket start at 0. for + // these metrics, a negative minimum will cause percentile metrics to be unavailable + if bounds.At(0) >= 0 { + em.minimum = max(em.minimum, 0.0) + } } + } if !dp.HasMax() { - bucketWidth := 0.01 // arbitrary width - there's no information about this histogram to make an inference with - if lenBounds > 1 { - bucketWidth = bounds.At(lenBounds-1) - bounds.At(lenBounds-2) + + // Find the last bucket with some data in it. The max must be in that bucket + maxBucketIdx := lenBounds - 1 + for i := lenBucketCounts - 1; i >= 0; i-- { + if bucketCounts.At(i) > 0 { + maxBucketIdx = i + break + } } - em.maximum = bounds.At(lenBounds-1) + bucketWidth + + // we want the upper bound of the bucket. the upper bound of bucket index n is boundary index n + if maxBucketIdx <= lenBounds-1 { + em.maximum = bounds.At(maxBucketIdx) + } else { + bucketWidth := 0.01 // arbitrary width - there's no information about this histogram to make an inference with + if lenBounds > 1 { + bucketWidth = bounds.At(lenBounds-1) - bounds.At(lenBounds-2) + } + em.maximum = bounds.At(lenBounds-1) + bucketWidth + } + } // Pre-calculate total output size to avoid dynamic growth From f8a73f45564709abb774ee6b0bc066b6fdea8b87 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Thu, 16 Oct 2025 13:11:07 -0400 Subject: [PATCH 14/23] Add histogram validity checker --- .../cloudwatch/histograms/test_cases_test.go | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pkg/aws/cloudwatch/histograms/test_cases_test.go b/pkg/aws/cloudwatch/histograms/test_cases_test.go index 5aad6d90996d8..920f835a1a263 100644 --- a/pkg/aws/cloudwatch/histograms/test_cases_test.go +++ b/pkg/aws/cloudwatch/histograms/test_cases_test.go @@ -96,6 +96,40 @@ func checkFeasibility(histogramInput HistogramInput) (bool, string) { } } + if hi.Max != nil { + if math.IsNaN(*hi.Max) { + return false, "max is NaN" + } + if math.IsInf(*hi.Max, 0) { + return false, "max is +/-inf" + } + } + + if hi.Max != nil { + if math.IsNaN(*hi.Min) { + return false, "min is NaN" + } + if math.IsInf(*hi.Min, 0) { + return false, "min is +/-inf" + } + } + + if math.IsNaN(hi.Sum) { + return false, "sum is NaN" + } + if math.IsInf(hi.Sum, 0) { + return false, "sum is +/-inf" + } + + for _, bound := range hi.Boundaries { + if math.IsNaN(bound) { + return false, "boundary is NaN" + } + if math.IsInf(bound, 0) { + return false, "boundary is +/-inf" + } + } + // Rest of checks only apply if we have boundaries/counts if lenBoundaries > 0 || lenCounts > 0 { // Check boundaries are in ascending order From 3f8c1dfb50c51bbabcef879d7dae4db8b35092a8 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Thu, 16 Oct 2025 13:11:58 -0400 Subject: [PATCH 15/23] Send all valid and invalid histogram test cases --- cmd/generator/main.go | 155 ++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 83 deletions(-) diff --git a/cmd/generator/main.go b/cmd/generator/main.go index ea0e68bff9b3a..8bbbada35495d 100644 --- a/cmd/generator/main.go +++ b/cmd/generator/main.go @@ -9,8 +9,6 @@ import ( "strings" "time" - types "github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/pkg" - "github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/pkg/metrics" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/aws/cloudwatch/histograms" "github.com/spf13/pflag" "go.opentelemetry.io/otel/attribute" @@ -20,7 +18,6 @@ import ( "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.uber.org/zap" - "golang.org/x/time/rate" ) const ( @@ -71,51 +68,13 @@ func (*KeyValue) Type() string { return "map[string]any" } -type ClientAuth struct { - Enabled bool - ClientCertFile string - ClientKeyFile string -} - // Config describes the test scenario. type Config struct { - WorkerCount int - Rate float64 - TotalDuration types.DurationWithInf - ReportingInterval time.Duration - SkipSettingGRPCLogger bool - - // OTLP config - CustomEndpoint string - Insecure bool - InsecureSkipVerify bool - UseHTTP bool - HTTPPath string - Headers KeyValue - ResourceAttributes KeyValue - ServiceName string - TelemetryAttributes KeyValue - - // OTLP TLS configuration - CaFile string - - // OTLP mTLS configuration - ClientAuth ClientAuth - - // Export behavior configuration - AllowExportFailures bool - - // Load testing configuration - LoadSize int - - NumMetrics int - MetricName string - MetricType metrics.MetricType - AggregationTemporality metricdata.Temporality - SpanID string - TraceID string - EnforceUniqueTimeseries bool - UniqueTimelimit time.Duration + CustomEndpoint string + Insecure bool + UseHTTP bool + HTTPPath string + Headers KeyValue } // Endpoint returns the appropriate endpoint URL based on the selected communication mode (gRPC or HTTP) @@ -147,8 +106,6 @@ func (c *Config) GetHeaders() map[string]string { func main() { - tc := histograms.TestCases()[0] - exporter, err := createExporter(&Config{ UseHTTP: true, Insecure: true, @@ -158,45 +115,56 @@ func main() { } res := resource.NewWithAttributes(semconv.SchemaURL) - limiter := rate.NewLimiter(1, 1) startTime := time.Now() - for { - if err := limiter.Wait(context.Background()); err != nil { - log.Print("limiter wait failed, retry", zap.Error(err)) - } - - attrs := []attribute.KeyValue{} - for k, v := range tc.Input.Attributes { - attrs = append(attrs, attribute.String(k, v)) + go func() { + testCases := histograms.TestCases() + ticker := time.NewTicker(time.Second * 10) + for range ticker.C { + for _, tc := range testCases { + metrics := []metricdata.Metrics{{ + Name: tc.Name, + Data: metricdata.Histogram[float64]{ + Temporality: metricdata.DeltaTemporality, + DataPoints: []metricdata.HistogramDataPoint[float64]{ + tcToDatapoint(tc, startTime), + }, + }, + }} + rm := metricdata.ResourceMetrics{ + Resource: res, + ScopeMetrics: []metricdata.ScopeMetrics{{Metrics: metrics}}, + } + + if err := exporter.Export(context.Background(), &rm); err != nil { + log.Fatal("exporter failed", zap.Error(err)) + } + } } - metrics := []metricdata.Metrics{{ - Name: tc.Name, - Data: metricdata.Histogram[float64]{ - Temporality: metricdata.DeltaTemporality, - DataPoints: []metricdata.HistogramDataPoint[float64]{ - { - StartTime: startTime, - Time: time.Now(), - Attributes: attribute.NewSet(attrs...), - Count: tc.Input.Count, - Sum: tc.Input.Sum, - Min: metricdata.NewExtrema(*tc.Input.Min), - Max: metricdata.NewExtrema(*tc.Input.Max), - Bounds: tc.Input.Boundaries, - BucketCounts: tc.Input.Counts, + }() + + ticker := time.NewTicker(time.Second * 10) + testCases := histograms.InvalidTestCases() + for range ticker.C { + for _, tc := range testCases { + metrics := []metricdata.Metrics{{ + Name: tc.Name, + Data: metricdata.Histogram[float64]{ + Temporality: metricdata.DeltaTemporality, + DataPoints: []metricdata.HistogramDataPoint[float64]{ + tcToDatapoint(tc, startTime), }, }, - }, - }} - rm := metricdata.ResourceMetrics{ - Resource: res, - ScopeMetrics: []metricdata.ScopeMetrics{{Metrics: metrics}}, - } - - if err := exporter.Export(context.Background(), &rm); err != nil { - log.Fatal("exporter failed", zap.Error(err)) + }} + rm := metricdata.ResourceMetrics{ + Resource: res, + ScopeMetrics: []metricdata.ScopeMetrics{{Metrics: metrics}}, + } + + if err := exporter.Export(context.Background(), &rm); err != nil { + log.Fatal("exporter failed", zap.Error(err)) + } } } @@ -242,6 +210,27 @@ func httpExporterOptions(cfg *Config) ([]otlpmetrichttp.Option, error) { return httpExpOpt, nil } -func ptr(f float64) *float64 { - return &f +func tcToDatapoint(tc histograms.HistogramTestCase, startTime time.Time) metricdata.HistogramDataPoint[float64] { + attrs := []attribute.KeyValue{} + for k, v := range tc.Input.Attributes { + attrs = append(attrs, attribute.String(k, v)) + } + + dp := metricdata.HistogramDataPoint[float64]{ + StartTime: startTime, + Time: time.Now(), + Attributes: attribute.NewSet(attrs...), + Count: tc.Input.Count, + Sum: tc.Input.Sum, + Bounds: tc.Input.Boundaries, + BucketCounts: tc.Input.Counts, + } + + if tc.Input.Min != nil { + dp.Min = metricdata.NewExtrema(*tc.Input.Min) + } + if tc.Input.Max != nil { + dp.Max = metricdata.NewExtrema(*tc.Input.Max) + } + return dp } From 5cac3bfcec37c046bb889e457d14d7ae670b97f7 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Thu, 16 Oct 2025 16:09:26 -0400 Subject: [PATCH 16/23] Update copyright header --- .../cloudwatch/histograms/conversion_test.go | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/pkg/aws/cloudwatch/histograms/conversion_test.go b/pkg/aws/cloudwatch/histograms/conversion_test.go index fd9a9e865efa3..8c58acd750ca2 100644 --- a/pkg/aws/cloudwatch/histograms/conversion_test.go +++ b/pkg/aws/cloudwatch/histograms/conversion_test.go @@ -20,6 +20,19 @@ import ( "go.opentelemetry.io/collector/pdata/pmetric" ) +var filenameReplacer = strings.NewReplacer( + " ", "_", + "/", "_", +) + +func TestWriteInputHistograms(t *testing.T) { + for _, tc := range TestCases() { + jsonData, err := json.MarshalIndent(tc.Input, "", " ") + require.NoError(t, err) + require.NoError(t, os.WriteFile("testdata/input/"+filenameReplacer.Replace(tc.Name)+".json", jsonData, 0644)) + } +} + func TestConvertOTelToCloudWatch(t *testing.T) { for _, tc := range TestCases() { @@ -30,11 +43,8 @@ func TestConvertOTelToCloudWatch(t *testing.T) { // uncomment next lines to write datapoint to JSON file for visual inspection // use histogram_mappings.py to create graphs - //var filenameReplacer = strings.NewReplacer( - // " ", "_", - // "/", "_", - //) - // assert.NoError(t, writeValuesAndCountsToJson(dist, filenameReplacer.Replace(tc.Name+".json"))) + + // assert.NoError(t, writeValuesAndCountsToJson(dist, "testdata/exponential/"+filenameReplacer.Replace(tc.Name+".json"))) }) } From 83a219bbef41cd0d9cf72f864a82610a146a5b35 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Thu, 16 Oct 2025 16:10:44 -0400 Subject: [PATCH 17/23] Remove generated testdata from version control --- .../testdata/exponential/126_Buckets.json | 2506 ------- .../testdata/exponential/176_Buckets.json | 3506 --------- .../testdata/exponential/225_Buckets.json | 4506 ------------ .../testdata/exponential/325_Buckets.json | 6506 ----------------- .../testdata/exponential/Basic_Histogram.json | 84 - .../Cumulative_bucket_starts_at_0.json | 204 - .../First_bucket_boundary_equals_minimum.json | 94 - .../testdata/exponential/Large_Numbers.json | 116 - .../testdata/exponential/Many_Buckets.json | 434 -- .../Negative_and_Positive_Boundaries.json | 78 - .../No_Min_Max_with_Single_Value.json | 8 - .../testdata/exponential/No_Min_or_Max.json | 70 - .../exponential/Only_Max_Defined.json | 90 - .../exponential/Only_Min_Defined.json | 60 - .../exponential/Only_Negative_Boundaries.json | 102 - ...oundaries_but_implied_Negative_Values.json | 102 - .../testdata/exponential/Single_Bucket.json | 8 - .../exponential/Tail_Heavy_Histogram.json | 128 - .../testdata/exponential/Two_Buckets.json | 34 - .../exponential/Unbounded_Histogram.json | 8 - .../exponential/Very_Small_Numbers.json | 92 - .../Zero_Counts_and_Sparse_Data.json | 46 - .../testdata/exponential/lognormal.json | 1058 --- .../testdata/exponential/weibull.json | 1024 --- 24 files changed, 20864 deletions(-) delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/126_Buckets.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/176_Buckets.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/225_Buckets.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/325_Buckets.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Basic_Histogram.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Cumulative_bucket_starts_at_0.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/First_bucket_boundary_equals_minimum.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Large_Numbers.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Many_Buckets.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Negative_and_Positive_Boundaries.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_Max_with_Single_Value.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_or_Max.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Max_Defined.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Min_Defined.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Negative_Boundaries.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Positive_boundaries_but_implied_Negative_Values.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Single_Bucket.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Tail_Heavy_Histogram.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Two_Buckets.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Unbounded_Histogram.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Very_Small_Numbers.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/Zero_Counts_and_Sparse_Data.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/lognormal.json delete mode 100644 pkg/aws/cloudwatch/histograms/testdata/exponential/weibull.json diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/126_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/126_Buckets.json deleted file mode 100644 index be9ec93916a74..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/126_Buckets.json +++ /dev/null @@ -1,2506 +0,0 @@ -{ - "counts": [ - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 3, - 3, - 2, - 2, - 1, - 3, - 3, - 2, - 2, - 1 - ], - "values": [ - 5.5, - 6, - 6.5, - 7, - 7.5, - 8, - 8.5, - 9, - 9.5, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 101, - 102, - 103, - 104, - 105, - 106, - 107, - 108, - 109, - 110, - 111, - 112, - 113, - 114, - 115, - 116, - 117, - 118, - 119, - 120, - 121, - 122, - 123, - 124, - 125, - 126, - 127, - 128, - 129, - 130, - 131, - 132, - 133, - 134, - 135, - 136, - 137, - 138, - 139, - 140, - 141, - 142, - 143, - 144, - 145, - 146, - 147, - 148, - 149, - 150, - 151, - 152, - 153, - 154, - 155, - 156, - 157, - 158, - 159, - 160, - 161, - 162, - 163, - 164, - 165, - 166, - 167, - 168, - 169, - 170, - 171, - 172, - 173, - 174, - 175, - 176, - 177, - 178, - 179, - 180, - 181, - 182, - 183, - 184, - 185, - 186, - 187, - 188, - 189, - 190, - 191, - 192, - 193, - 194, - 195, - 196, - 197, - 198, - 199, - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 209, - 210, - 211, - 212, - 213, - 214, - 215, - 216, - 217, - 218, - 219, - 220, - 221, - 222, - 223, - 224, - 225, - 226, - 227, - 228, - 229, - 230, - 231, - 232, - 233, - 234, - 235, - 236, - 237, - 238, - 239, - 240, - 241, - 242, - 243, - 244, - 245, - 246, - 247, - 248, - 249, - 250, - 251, - 252, - 253, - 254, - 255, - 256, - 257, - 258, - 259, - 260, - 261, - 262, - 263, - 264, - 265, - 266, - 267, - 268, - 269, - 270, - 271, - 272, - 273, - 274, - 275, - 276, - 277, - 278, - 279, - 280, - 281, - 282, - 283, - 284, - 285, - 286, - 287, - 288, - 289, - 290, - 291, - 292, - 293, - 294, - 295, - 296, - 297, - 298, - 299, - 300, - 301, - 302, - 303, - 304, - 305, - 306, - 307, - 308, - 309, - 310, - 311, - 312, - 313, - 314, - 315, - 316, - 317, - 318, - 319, - 320, - 321, - 322, - 323, - 324, - 325, - 326, - 327, - 328, - 329, - 330, - 331, - 332, - 333, - 334, - 335, - 336, - 337, - 338, - 339, - 340, - 341, - 342, - 343, - 344, - 345, - 346, - 347, - 348, - 349, - 350, - 351, - 352, - 353, - 354, - 355, - 356, - 357, - 358, - 359, - 360, - 361, - 362, - 363, - 364, - 365, - 366, - 367, - 368, - 369, - 370, - 371, - 372, - 373, - 374, - 375, - 376, - 377, - 378, - 379, - 380, - 381, - 382, - 383, - 384, - 385, - 386, - 387, - 388, - 389, - 390, - 391, - 392, - 393, - 394, - 395, - 396, - 397, - 398, - 399, - 400, - 401, - 402, - 403, - 404, - 405, - 406, - 407, - 408, - 409, - 410, - 411, - 412, - 413, - 414, - 415, - 416, - 417, - 418, - 419, - 420, - 421, - 422, - 423, - 424, - 425, - 426, - 427, - 428, - 429, - 430, - 431, - 432, - 433, - 434, - 435, - 436, - 437, - 438, - 439, - 440, - 441, - 442, - 443, - 444, - 445, - 446, - 447, - 448, - 449, - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 466, - 467, - 468, - 469, - 470, - 471, - 472, - 473, - 474, - 475, - 476, - 477, - 478, - 479, - 480, - 481, - 482, - 483, - 484, - 485, - 486, - 487, - 488, - 489, - 490, - 491, - 492, - 493, - 494, - 495, - 496, - 497, - 498, - 499, - 500, - 501, - 502, - 503, - 504, - 505, - 506, - 507, - 508, - 509, - 510, - 511, - 512, - 513, - 514, - 515, - 516, - 517, - 518, - 519, - 520, - 521, - 522, - 523, - 524, - 525, - 526, - 527, - 528, - 529, - 530, - 531, - 532, - 533, - 534, - 535, - 536, - 537, - 538, - 539, - 540, - 541, - 542, - 543, - 544, - 545, - 546, - 547, - 548, - 549, - 550, - 551, - 552, - 553, - 554, - 555, - 556, - 557, - 558, - 559, - 560, - 561, - 562, - 563, - 564, - 565, - 566, - 567, - 568, - 569, - 570, - 571, - 572, - 573, - 574, - 575, - 576, - 577, - 578, - 579, - 580, - 581, - 582, - 583, - 584, - 585, - 586, - 587, - 588, - 589, - 590, - 591, - 592, - 593, - 594, - 595, - 596, - 597, - 598, - 599, - 600, - 601, - 602, - 603, - 604, - 605, - 606, - 607, - 608, - 609, - 610, - 611, - 612, - 613, - 614, - 615, - 616, - 617, - 618, - 619, - 620, - 621, - 622, - 623, - 624, - 625, - 626, - 627, - 628, - 629, - 630, - 631, - 632, - 633, - 634, - 635, - 636, - 637, - 638, - 639, - 640, - 641, - 642, - 643, - 644, - 645, - 646, - 647, - 648, - 649, - 650, - 651, - 652, - 653, - 654, - 655, - 656, - 657, - 658, - 659, - 660, - 661, - 662, - 663, - 664, - 665, - 666, - 667, - 668, - 669, - 670, - 671, - 672, - 673, - 674, - 675, - 676, - 677, - 678, - 679, - 680, - 681, - 682, - 683, - 684, - 685, - 686, - 687, - 688, - 689, - 690, - 691, - 692, - 693, - 694, - 695, - 696, - 697, - 698, - 699, - 700, - 701, - 702, - 703, - 704, - 705, - 706, - 707, - 708, - 709, - 710, - 711, - 712, - 713, - 714, - 715, - 716, - 717, - 718, - 719, - 720, - 721, - 722, - 723, - 724, - 725, - 726, - 727, - 728, - 729, - 730, - 731, - 732, - 733, - 734, - 735, - 736, - 737, - 738, - 739, - 740, - 741, - 742, - 743, - 744, - 745, - 746, - 747, - 748, - 749, - 750, - 751, - 752, - 753, - 754, - 755, - 756, - 757, - 758, - 759, - 760, - 761, - 762, - 763, - 764, - 765, - 766, - 767, - 768, - 769, - 770, - 771, - 772, - 773, - 774, - 775, - 776, - 777, - 778, - 779, - 780, - 781, - 782, - 783, - 784, - 785, - 786, - 787, - 788, - 789, - 790, - 791, - 792, - 793, - 794, - 795, - 796, - 797, - 798, - 799, - 800, - 801, - 802, - 803, - 804, - 805, - 806, - 807, - 808, - 809, - 810, - 811, - 812, - 813, - 814, - 815, - 816, - 817, - 818, - 819, - 820, - 821, - 822, - 823, - 824, - 825, - 826, - 827, - 828, - 829, - 830, - 831, - 832, - 833, - 834, - 835, - 836, - 837, - 838, - 839, - 840, - 841, - 842, - 843, - 844, - 845, - 846, - 847, - 848, - 849, - 850, - 851, - 852, - 853, - 854, - 855, - 856, - 857, - 858, - 859, - 860, - 861, - 862, - 863, - 864, - 865, - 866, - 867, - 868, - 869, - 870, - 871, - 872, - 873, - 874, - 875, - 876, - 877, - 878, - 879, - 880, - 881, - 882, - 883, - 884, - 885, - 886, - 887, - 888, - 889, - 890, - 891, - 892, - 893, - 894, - 895, - 896, - 897, - 898, - 899, - 900, - 901, - 902, - 903, - 904, - 905, - 906, - 907, - 908, - 909, - 910, - 911, - 912, - 913, - 914, - 915, - 916, - 917, - 918, - 919, - 920, - 921, - 922, - 923, - 924, - 925, - 926, - 927, - 928, - 929, - 930, - 931, - 932, - 933, - 934, - 935, - 936, - 937, - 938, - 939, - 940, - 941, - 942, - 943, - 944, - 945, - 946, - 947, - 948, - 949, - 950, - 951, - 952, - 953, - 954, - 955, - 956, - 957, - 958, - 959, - 960, - 961, - 962, - 963, - 964, - 965, - 966, - 967, - 968, - 969, - 970, - 971, - 972, - 973, - 974, - 975, - 976, - 977, - 978, - 979, - 980, - 981, - 982, - 983, - 984, - 985, - 986, - 987, - 988, - 989, - 990, - 991, - 992, - 993, - 994, - 995, - 996, - 997, - 998, - 999, - 1000, - 1001, - 1002, - 1003, - 1004, - 1005, - 1006, - 1007, - 1008, - 1009, - 1010, - 1011, - 1012, - 1013, - 1014, - 1015, - 1016, - 1017, - 1018, - 1019, - 1020, - 1021, - 1022, - 1023, - 1024, - 1025, - 1026, - 1027, - 1028, - 1029, - 1030, - 1031, - 1032, - 1033, - 1034, - 1035, - 1036, - 1037, - 1038, - 1039, - 1040, - 1041, - 1042, - 1043, - 1044, - 1045, - 1046, - 1047, - 1048, - 1049, - 1050, - 1051, - 1052, - 1053, - 1054, - 1055, - 1056, - 1057, - 1058, - 1059, - 1060, - 1061, - 1062, - 1063, - 1064, - 1065, - 1066, - 1067, - 1068, - 1069, - 1070, - 1071, - 1072, - 1073, - 1074, - 1075, - 1076, - 1077, - 1078, - 1079, - 1080, - 1081, - 1082, - 1083, - 1084, - 1085, - 1086, - 1087, - 1088, - 1089, - 1090, - 1091, - 1092, - 1093, - 1094, - 1095, - 1096, - 1097, - 1098, - 1099, - 1100, - 1101, - 1102, - 1103, - 1104, - 1105, - 1106, - 1107, - 1108, - 1109, - 1110, - 1111, - 1112, - 1113, - 1114, - 1115, - 1116, - 1117, - 1118, - 1119, - 1120, - 1121, - 1122, - 1123, - 1124, - 1125, - 1126, - 1127, - 1128, - 1129, - 1130, - 1131, - 1132, - 1133, - 1134, - 1135, - 1136, - 1137, - 1138, - 1139, - 1140, - 1141, - 1142, - 1143, - 1144, - 1145, - 1146, - 1147, - 1148, - 1149, - 1150, - 1151, - 1152, - 1153, - 1154, - 1155, - 1156, - 1157, - 1158, - 1159, - 1160, - 1161, - 1162, - 1163, - 1164, - 1165, - 1166, - 1167, - 1168, - 1169, - 1170, - 1171, - 1172, - 1173, - 1174, - 1175, - 1176, - 1177, - 1178, - 1179, - 1180, - 1181, - 1182, - 1183, - 1184, - 1185, - 1186, - 1187, - 1188, - 1189, - 1190, - 1191, - 1192, - 1193, - 1194, - 1195, - 1196, - 1197, - 1198, - 1199, - 1200, - 1201, - 1202, - 1203, - 1204, - 1205, - 1206, - 1207, - 1208, - 1209, - 1210, - 1211, - 1212, - 1213, - 1214, - 1215, - 1216, - 1217, - 1218, - 1219, - 1220, - 1221, - 1222, - 1223, - 1224, - 1225, - 1226, - 1227, - 1228, - 1229, - 1230, - 1231, - 1232, - 1233, - 1234, - 1235, - 1236, - 1237, - 1238, - 1239, - 1240, - 1241, - 1242, - 1243, - 1244, - 1245, - 1255, - 1260, - 1265, - 1270, - 1300 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/176_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/176_Buckets.json deleted file mode 100644 index 7fefaa6d88591..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/176_Buckets.json +++ /dev/null @@ -1,3506 +0,0 @@ -{ - "counts": [ - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 3, - 3, - 2, - 2, - 1, - 3, - 3, - 2, - 2, - 1 - ], - "values": [ - 5.5, - 6, - 6.5, - 7, - 7.5, - 8, - 8.5, - 9, - 9.5, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 101, - 102, - 103, - 104, - 105, - 106, - 107, - 108, - 109, - 110, - 111, - 112, - 113, - 114, - 115, - 116, - 117, - 118, - 119, - 120, - 121, - 122, - 123, - 124, - 125, - 126, - 127, - 128, - 129, - 130, - 131, - 132, - 133, - 134, - 135, - 136, - 137, - 138, - 139, - 140, - 141, - 142, - 143, - 144, - 145, - 146, - 147, - 148, - 149, - 150, - 151, - 152, - 153, - 154, - 155, - 156, - 157, - 158, - 159, - 160, - 161, - 162, - 163, - 164, - 165, - 166, - 167, - 168, - 169, - 170, - 171, - 172, - 173, - 174, - 175, - 176, - 177, - 178, - 179, - 180, - 181, - 182, - 183, - 184, - 185, - 186, - 187, - 188, - 189, - 190, - 191, - 192, - 193, - 194, - 195, - 196, - 197, - 198, - 199, - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 209, - 210, - 211, - 212, - 213, - 214, - 215, - 216, - 217, - 218, - 219, - 220, - 221, - 222, - 223, - 224, - 225, - 226, - 227, - 228, - 229, - 230, - 231, - 232, - 233, - 234, - 235, - 236, - 237, - 238, - 239, - 240, - 241, - 242, - 243, - 244, - 245, - 246, - 247, - 248, - 249, - 250, - 251, - 252, - 253, - 254, - 255, - 256, - 257, - 258, - 259, - 260, - 261, - 262, - 263, - 264, - 265, - 266, - 267, - 268, - 269, - 270, - 271, - 272, - 273, - 274, - 275, - 276, - 277, - 278, - 279, - 280, - 281, - 282, - 283, - 284, - 285, - 286, - 287, - 288, - 289, - 290, - 291, - 292, - 293, - 294, - 295, - 296, - 297, - 298, - 299, - 300, - 301, - 302, - 303, - 304, - 305, - 306, - 307, - 308, - 309, - 310, - 311, - 312, - 313, - 314, - 315, - 316, - 317, - 318, - 319, - 320, - 321, - 322, - 323, - 324, - 325, - 326, - 327, - 328, - 329, - 330, - 331, - 332, - 333, - 334, - 335, - 336, - 337, - 338, - 339, - 340, - 341, - 342, - 343, - 344, - 345, - 346, - 347, - 348, - 349, - 350, - 351, - 352, - 353, - 354, - 355, - 356, - 357, - 358, - 359, - 360, - 361, - 362, - 363, - 364, - 365, - 366, - 367, - 368, - 369, - 370, - 371, - 372, - 373, - 374, - 375, - 376, - 377, - 378, - 379, - 380, - 381, - 382, - 383, - 384, - 385, - 386, - 387, - 388, - 389, - 390, - 391, - 392, - 393, - 394, - 395, - 396, - 397, - 398, - 399, - 400, - 401, - 402, - 403, - 404, - 405, - 406, - 407, - 408, - 409, - 410, - 411, - 412, - 413, - 414, - 415, - 416, - 417, - 418, - 419, - 420, - 421, - 422, - 423, - 424, - 425, - 426, - 427, - 428, - 429, - 430, - 431, - 432, - 433, - 434, - 435, - 436, - 437, - 438, - 439, - 440, - 441, - 442, - 443, - 444, - 445, - 446, - 447, - 448, - 449, - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 466, - 467, - 468, - 469, - 470, - 471, - 472, - 473, - 474, - 475, - 476, - 477, - 478, - 479, - 480, - 481, - 482, - 483, - 484, - 485, - 486, - 487, - 488, - 489, - 490, - 491, - 492, - 493, - 494, - 495, - 496, - 497, - 498, - 499, - 500, - 501, - 502, - 503, - 504, - 505, - 506, - 507, - 508, - 509, - 510, - 511, - 512, - 513, - 514, - 515, - 516, - 517, - 518, - 519, - 520, - 521, - 522, - 523, - 524, - 525, - 526, - 527, - 528, - 529, - 530, - 531, - 532, - 533, - 534, - 535, - 536, - 537, - 538, - 539, - 540, - 541, - 542, - 543, - 544, - 545, - 546, - 547, - 548, - 549, - 550, - 551, - 552, - 553, - 554, - 555, - 556, - 557, - 558, - 559, - 560, - 561, - 562, - 563, - 564, - 565, - 566, - 567, - 568, - 569, - 570, - 571, - 572, - 573, - 574, - 575, - 576, - 577, - 578, - 579, - 580, - 581, - 582, - 583, - 584, - 585, - 586, - 587, - 588, - 589, - 590, - 591, - 592, - 593, - 594, - 595, - 596, - 597, - 598, - 599, - 600, - 601, - 602, - 603, - 604, - 605, - 606, - 607, - 608, - 609, - 610, - 611, - 612, - 613, - 614, - 615, - 616, - 617, - 618, - 619, - 620, - 621, - 622, - 623, - 624, - 625, - 626, - 627, - 628, - 629, - 630, - 631, - 632, - 633, - 634, - 635, - 636, - 637, - 638, - 639, - 640, - 641, - 642, - 643, - 644, - 645, - 646, - 647, - 648, - 649, - 650, - 651, - 652, - 653, - 654, - 655, - 656, - 657, - 658, - 659, - 660, - 661, - 662, - 663, - 664, - 665, - 666, - 667, - 668, - 669, - 670, - 671, - 672, - 673, - 674, - 675, - 676, - 677, - 678, - 679, - 680, - 681, - 682, - 683, - 684, - 685, - 686, - 687, - 688, - 689, - 690, - 691, - 692, - 693, - 694, - 695, - 696, - 697, - 698, - 699, - 700, - 701, - 702, - 703, - 704, - 705, - 706, - 707, - 708, - 709, - 710, - 711, - 712, - 713, - 714, - 715, - 716, - 717, - 718, - 719, - 720, - 721, - 722, - 723, - 724, - 725, - 726, - 727, - 728, - 729, - 730, - 731, - 732, - 733, - 734, - 735, - 736, - 737, - 738, - 739, - 740, - 741, - 742, - 743, - 744, - 745, - 746, - 747, - 748, - 749, - 750, - 751, - 752, - 753, - 754, - 755, - 756, - 757, - 758, - 759, - 760, - 761, - 762, - 763, - 764, - 765, - 766, - 767, - 768, - 769, - 770, - 771, - 772, - 773, - 774, - 775, - 776, - 777, - 778, - 779, - 780, - 781, - 782, - 783, - 784, - 785, - 786, - 787, - 788, - 789, - 790, - 791, - 792, - 793, - 794, - 795, - 796, - 797, - 798, - 799, - 800, - 801, - 802, - 803, - 804, - 805, - 806, - 807, - 808, - 809, - 810, - 811, - 812, - 813, - 814, - 815, - 816, - 817, - 818, - 819, - 820, - 821, - 822, - 823, - 824, - 825, - 826, - 827, - 828, - 829, - 830, - 831, - 832, - 833, - 834, - 835, - 836, - 837, - 838, - 839, - 840, - 841, - 842, - 843, - 844, - 845, - 846, - 847, - 848, - 849, - 850, - 851, - 852, - 853, - 854, - 855, - 856, - 857, - 858, - 859, - 860, - 861, - 862, - 863, - 864, - 865, - 866, - 867, - 868, - 869, - 870, - 871, - 872, - 873, - 874, - 875, - 876, - 877, - 878, - 879, - 880, - 881, - 882, - 883, - 884, - 885, - 886, - 887, - 888, - 889, - 890, - 891, - 892, - 893, - 894, - 895, - 896, - 897, - 898, - 899, - 900, - 901, - 902, - 903, - 904, - 905, - 906, - 907, - 908, - 909, - 910, - 911, - 912, - 913, - 914, - 915, - 916, - 917, - 918, - 919, - 920, - 921, - 922, - 923, - 924, - 925, - 926, - 927, - 928, - 929, - 930, - 931, - 932, - 933, - 934, - 935, - 936, - 937, - 938, - 939, - 940, - 941, - 942, - 943, - 944, - 945, - 946, - 947, - 948, - 949, - 950, - 951, - 952, - 953, - 954, - 955, - 956, - 957, - 958, - 959, - 960, - 961, - 962, - 963, - 964, - 965, - 966, - 967, - 968, - 969, - 970, - 971, - 972, - 973, - 974, - 975, - 976, - 977, - 978, - 979, - 980, - 981, - 982, - 983, - 984, - 985, - 986, - 987, - 988, - 989, - 990, - 991, - 992, - 993, - 994, - 995, - 996, - 997, - 998, - 999, - 1000, - 1001, - 1002, - 1003, - 1004, - 1005, - 1006, - 1007, - 1008, - 1009, - 1010, - 1011, - 1012, - 1013, - 1014, - 1015, - 1016, - 1017, - 1018, - 1019, - 1020, - 1021, - 1022, - 1023, - 1024, - 1025, - 1026, - 1027, - 1028, - 1029, - 1030, - 1031, - 1032, - 1033, - 1034, - 1035, - 1036, - 1037, - 1038, - 1039, - 1040, - 1041, - 1042, - 1043, - 1044, - 1045, - 1046, - 1047, - 1048, - 1049, - 1050, - 1051, - 1052, - 1053, - 1054, - 1055, - 1056, - 1057, - 1058, - 1059, - 1060, - 1061, - 1062, - 1063, - 1064, - 1065, - 1066, - 1067, - 1068, - 1069, - 1070, - 1071, - 1072, - 1073, - 1074, - 1075, - 1076, - 1077, - 1078, - 1079, - 1080, - 1081, - 1082, - 1083, - 1084, - 1085, - 1086, - 1087, - 1088, - 1089, - 1090, - 1091, - 1092, - 1093, - 1094, - 1095, - 1096, - 1097, - 1098, - 1099, - 1100, - 1101, - 1102, - 1103, - 1104, - 1105, - 1106, - 1107, - 1108, - 1109, - 1110, - 1111, - 1112, - 1113, - 1114, - 1115, - 1116, - 1117, - 1118, - 1119, - 1120, - 1121, - 1122, - 1123, - 1124, - 1125, - 1126, - 1127, - 1128, - 1129, - 1130, - 1131, - 1132, - 1133, - 1134, - 1135, - 1136, - 1137, - 1138, - 1139, - 1140, - 1141, - 1142, - 1143, - 1144, - 1145, - 1146, - 1147, - 1148, - 1149, - 1150, - 1151, - 1152, - 1153, - 1154, - 1155, - 1156, - 1157, - 1158, - 1159, - 1160, - 1161, - 1162, - 1163, - 1164, - 1165, - 1166, - 1167, - 1168, - 1169, - 1170, - 1171, - 1172, - 1173, - 1174, - 1175, - 1176, - 1177, - 1178, - 1179, - 1180, - 1181, - 1182, - 1183, - 1184, - 1185, - 1186, - 1187, - 1188, - 1189, - 1190, - 1191, - 1192, - 1193, - 1194, - 1195, - 1196, - 1197, - 1198, - 1199, - 1200, - 1201, - 1202, - 1203, - 1204, - 1205, - 1206, - 1207, - 1208, - 1209, - 1210, - 1211, - 1212, - 1213, - 1214, - 1215, - 1216, - 1217, - 1218, - 1219, - 1220, - 1221, - 1222, - 1223, - 1224, - 1225, - 1226, - 1227, - 1228, - 1229, - 1230, - 1231, - 1232, - 1233, - 1234, - 1235, - 1236, - 1237, - 1238, - 1239, - 1240, - 1241, - 1242, - 1243, - 1244, - 1245, - 1246, - 1247, - 1248, - 1249, - 1250, - 1251, - 1252, - 1253, - 1254, - 1255, - 1256, - 1257, - 1258, - 1259, - 1260, - 1261, - 1262, - 1263, - 1264, - 1265, - 1266, - 1267, - 1268, - 1269, - 1270, - 1271, - 1272, - 1273, - 1274, - 1275, - 1276, - 1277, - 1278, - 1279, - 1280, - 1281, - 1282, - 1283, - 1284, - 1285, - 1286, - 1287, - 1288, - 1289, - 1290, - 1291, - 1292, - 1293, - 1294, - 1295, - 1296, - 1297, - 1298, - 1299, - 1300, - 1301, - 1302, - 1303, - 1304, - 1305, - 1306, - 1307, - 1308, - 1309, - 1310, - 1311, - 1312, - 1313, - 1314, - 1315, - 1316, - 1317, - 1318, - 1319, - 1320, - 1321, - 1322, - 1323, - 1324, - 1325, - 1326, - 1327, - 1328, - 1329, - 1330, - 1331, - 1332, - 1333, - 1334, - 1335, - 1336, - 1337, - 1338, - 1339, - 1340, - 1341, - 1342, - 1343, - 1344, - 1345, - 1346, - 1347, - 1348, - 1349, - 1350, - 1351, - 1352, - 1353, - 1354, - 1355, - 1356, - 1357, - 1358, - 1359, - 1360, - 1361, - 1362, - 1363, - 1364, - 1365, - 1366, - 1367, - 1368, - 1369, - 1370, - 1371, - 1372, - 1373, - 1374, - 1375, - 1376, - 1377, - 1378, - 1379, - 1380, - 1381, - 1382, - 1383, - 1384, - 1385, - 1386, - 1387, - 1388, - 1389, - 1390, - 1391, - 1392, - 1393, - 1394, - 1395, - 1396, - 1397, - 1398, - 1399, - 1400, - 1401, - 1402, - 1403, - 1404, - 1405, - 1406, - 1407, - 1408, - 1409, - 1410, - 1411, - 1412, - 1413, - 1414, - 1415, - 1416, - 1417, - 1418, - 1419, - 1420, - 1421, - 1422, - 1423, - 1424, - 1425, - 1426, - 1427, - 1428, - 1429, - 1430, - 1431, - 1432, - 1433, - 1434, - 1435, - 1436, - 1437, - 1438, - 1439, - 1440, - 1441, - 1442, - 1443, - 1444, - 1445, - 1446, - 1447, - 1448, - 1449, - 1450, - 1451, - 1452, - 1453, - 1454, - 1455, - 1456, - 1457, - 1458, - 1459, - 1460, - 1461, - 1462, - 1463, - 1464, - 1465, - 1466, - 1467, - 1468, - 1469, - 1470, - 1471, - 1472, - 1473, - 1474, - 1475, - 1476, - 1477, - 1478, - 1479, - 1480, - 1481, - 1482, - 1483, - 1484, - 1485, - 1486, - 1487, - 1488, - 1489, - 1490, - 1491, - 1492, - 1493, - 1494, - 1495, - 1496, - 1497, - 1498, - 1499, - 1500, - 1501, - 1502, - 1503, - 1504, - 1505, - 1506, - 1507, - 1508, - 1509, - 1510, - 1511, - 1512, - 1513, - 1514, - 1515, - 1516, - 1517, - 1518, - 1519, - 1520, - 1521, - 1522, - 1523, - 1524, - 1525, - 1526, - 1527, - 1528, - 1529, - 1530, - 1531, - 1532, - 1533, - 1534, - 1535, - 1536, - 1537, - 1538, - 1539, - 1540, - 1541, - 1542, - 1543, - 1544, - 1545, - 1546, - 1547, - 1548, - 1549, - 1550, - 1551, - 1552, - 1553, - 1554, - 1555, - 1556, - 1557, - 1558, - 1559, - 1560, - 1561, - 1562, - 1563, - 1564, - 1565, - 1566, - 1567, - 1568, - 1569, - 1570, - 1571, - 1572, - 1573, - 1574, - 1575, - 1576, - 1577, - 1578, - 1579, - 1580, - 1581, - 1582, - 1583, - 1584, - 1585, - 1586, - 1587, - 1588, - 1589, - 1590, - 1591, - 1592, - 1593, - 1594, - 1595, - 1596, - 1597, - 1598, - 1599, - 1600, - 1601, - 1602, - 1603, - 1604, - 1605, - 1606, - 1607, - 1608, - 1609, - 1610, - 1611, - 1612, - 1613, - 1614, - 1615, - 1616, - 1617, - 1618, - 1619, - 1620, - 1621, - 1622, - 1623, - 1624, - 1625, - 1626, - 1627, - 1628, - 1629, - 1630, - 1631, - 1632, - 1633, - 1634, - 1635, - 1636, - 1637, - 1638, - 1639, - 1640, - 1641, - 1642, - 1643, - 1644, - 1645, - 1646, - 1647, - 1648, - 1649, - 1650, - 1651, - 1652, - 1653, - 1654, - 1655, - 1656, - 1657, - 1658, - 1659, - 1660, - 1661, - 1662, - 1663, - 1664, - 1665, - 1666, - 1667, - 1668, - 1669, - 1670, - 1671, - 1672, - 1673, - 1674, - 1675, - 1676, - 1677, - 1678, - 1679, - 1680, - 1681, - 1682, - 1683, - 1684, - 1685, - 1686, - 1687, - 1688, - 1689, - 1690, - 1691, - 1692, - 1693, - 1694, - 1695, - 1696, - 1697, - 1698, - 1699, - 1700, - 1701, - 1702, - 1703, - 1704, - 1705, - 1706, - 1707, - 1708, - 1709, - 1710, - 1711, - 1712, - 1713, - 1714, - 1715, - 1716, - 1717, - 1718, - 1719, - 1720, - 1721, - 1722, - 1723, - 1724, - 1725, - 1726, - 1727, - 1728, - 1729, - 1730, - 1731, - 1732, - 1733, - 1734, - 1735, - 1736, - 1737, - 1738, - 1739, - 1740, - 1741, - 1742, - 1743, - 1744, - 1745, - 1755, - 1760, - 1765, - 1770, - 1800 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/225_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/225_Buckets.json deleted file mode 100644 index 787c23e12554c..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/225_Buckets.json +++ /dev/null @@ -1,4506 +0,0 @@ -{ - "counts": [ - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 3, - 3, - 2, - 2, - 1, - 3, - 3, - 2, - 2, - 1 - ], - "values": [ - 5.5, - 6, - 6.5, - 7, - 7.5, - 8, - 8.5, - 9, - 9.5, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 101, - 102, - 103, - 104, - 105, - 106, - 107, - 108, - 109, - 110, - 111, - 112, - 113, - 114, - 115, - 116, - 117, - 118, - 119, - 120, - 121, - 122, - 123, - 124, - 125, - 126, - 127, - 128, - 129, - 130, - 131, - 132, - 133, - 134, - 135, - 136, - 137, - 138, - 139, - 140, - 141, - 142, - 143, - 144, - 145, - 146, - 147, - 148, - 149, - 150, - 151, - 152, - 153, - 154, - 155, - 156, - 157, - 158, - 159, - 160, - 161, - 162, - 163, - 164, - 165, - 166, - 167, - 168, - 169, - 170, - 171, - 172, - 173, - 174, - 175, - 176, - 177, - 178, - 179, - 180, - 181, - 182, - 183, - 184, - 185, - 186, - 187, - 188, - 189, - 190, - 191, - 192, - 193, - 194, - 195, - 196, - 197, - 198, - 199, - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 209, - 210, - 211, - 212, - 213, - 214, - 215, - 216, - 217, - 218, - 219, - 220, - 221, - 222, - 223, - 224, - 225, - 226, - 227, - 228, - 229, - 230, - 231, - 232, - 233, - 234, - 235, - 236, - 237, - 238, - 239, - 240, - 241, - 242, - 243, - 244, - 245, - 246, - 247, - 248, - 249, - 250, - 251, - 252, - 253, - 254, - 255, - 256, - 257, - 258, - 259, - 260, - 261, - 262, - 263, - 264, - 265, - 266, - 267, - 268, - 269, - 270, - 271, - 272, - 273, - 274, - 275, - 276, - 277, - 278, - 279, - 280, - 281, - 282, - 283, - 284, - 285, - 286, - 287, - 288, - 289, - 290, - 291, - 292, - 293, - 294, - 295, - 296, - 297, - 298, - 299, - 300, - 301, - 302, - 303, - 304, - 305, - 306, - 307, - 308, - 309, - 310, - 311, - 312, - 313, - 314, - 315, - 316, - 317, - 318, - 319, - 320, - 321, - 322, - 323, - 324, - 325, - 326, - 327, - 328, - 329, - 330, - 331, - 332, - 333, - 334, - 335, - 336, - 337, - 338, - 339, - 340, - 341, - 342, - 343, - 344, - 345, - 346, - 347, - 348, - 349, - 350, - 351, - 352, - 353, - 354, - 355, - 356, - 357, - 358, - 359, - 360, - 361, - 362, - 363, - 364, - 365, - 366, - 367, - 368, - 369, - 370, - 371, - 372, - 373, - 374, - 375, - 376, - 377, - 378, - 379, - 380, - 381, - 382, - 383, - 384, - 385, - 386, - 387, - 388, - 389, - 390, - 391, - 392, - 393, - 394, - 395, - 396, - 397, - 398, - 399, - 400, - 401, - 402, - 403, - 404, - 405, - 406, - 407, - 408, - 409, - 410, - 411, - 412, - 413, - 414, - 415, - 416, - 417, - 418, - 419, - 420, - 421, - 422, - 423, - 424, - 425, - 426, - 427, - 428, - 429, - 430, - 431, - 432, - 433, - 434, - 435, - 436, - 437, - 438, - 439, - 440, - 441, - 442, - 443, - 444, - 445, - 446, - 447, - 448, - 449, - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 466, - 467, - 468, - 469, - 470, - 471, - 472, - 473, - 474, - 475, - 476, - 477, - 478, - 479, - 480, - 481, - 482, - 483, - 484, - 485, - 486, - 487, - 488, - 489, - 490, - 491, - 492, - 493, - 494, - 495, - 496, - 497, - 498, - 499, - 500, - 501, - 502, - 503, - 504, - 505, - 506, - 507, - 508, - 509, - 510, - 511, - 512, - 513, - 514, - 515, - 516, - 517, - 518, - 519, - 520, - 521, - 522, - 523, - 524, - 525, - 526, - 527, - 528, - 529, - 530, - 531, - 532, - 533, - 534, - 535, - 536, - 537, - 538, - 539, - 540, - 541, - 542, - 543, - 544, - 545, - 546, - 547, - 548, - 549, - 550, - 551, - 552, - 553, - 554, - 555, - 556, - 557, - 558, - 559, - 560, - 561, - 562, - 563, - 564, - 565, - 566, - 567, - 568, - 569, - 570, - 571, - 572, - 573, - 574, - 575, - 576, - 577, - 578, - 579, - 580, - 581, - 582, - 583, - 584, - 585, - 586, - 587, - 588, - 589, - 590, - 591, - 592, - 593, - 594, - 595, - 596, - 597, - 598, - 599, - 600, - 601, - 602, - 603, - 604, - 605, - 606, - 607, - 608, - 609, - 610, - 611, - 612, - 613, - 614, - 615, - 616, - 617, - 618, - 619, - 620, - 621, - 622, - 623, - 624, - 625, - 626, - 627, - 628, - 629, - 630, - 631, - 632, - 633, - 634, - 635, - 636, - 637, - 638, - 639, - 640, - 641, - 642, - 643, - 644, - 645, - 646, - 647, - 648, - 649, - 650, - 651, - 652, - 653, - 654, - 655, - 656, - 657, - 658, - 659, - 660, - 661, - 662, - 663, - 664, - 665, - 666, - 667, - 668, - 669, - 670, - 671, - 672, - 673, - 674, - 675, - 676, - 677, - 678, - 679, - 680, - 681, - 682, - 683, - 684, - 685, - 686, - 687, - 688, - 689, - 690, - 691, - 692, - 693, - 694, - 695, - 696, - 697, - 698, - 699, - 700, - 701, - 702, - 703, - 704, - 705, - 706, - 707, - 708, - 709, - 710, - 711, - 712, - 713, - 714, - 715, - 716, - 717, - 718, - 719, - 720, - 721, - 722, - 723, - 724, - 725, - 726, - 727, - 728, - 729, - 730, - 731, - 732, - 733, - 734, - 735, - 736, - 737, - 738, - 739, - 740, - 741, - 742, - 743, - 744, - 745, - 746, - 747, - 748, - 749, - 750, - 751, - 752, - 753, - 754, - 755, - 756, - 757, - 758, - 759, - 760, - 761, - 762, - 763, - 764, - 765, - 766, - 767, - 768, - 769, - 770, - 771, - 772, - 773, - 774, - 775, - 776, - 777, - 778, - 779, - 780, - 781, - 782, - 783, - 784, - 785, - 786, - 787, - 788, - 789, - 790, - 791, - 792, - 793, - 794, - 795, - 796, - 797, - 798, - 799, - 800, - 801, - 802, - 803, - 804, - 805, - 806, - 807, - 808, - 809, - 810, - 811, - 812, - 813, - 814, - 815, - 816, - 817, - 818, - 819, - 820, - 821, - 822, - 823, - 824, - 825, - 826, - 827, - 828, - 829, - 830, - 831, - 832, - 833, - 834, - 835, - 836, - 837, - 838, - 839, - 840, - 841, - 842, - 843, - 844, - 845, - 846, - 847, - 848, - 849, - 850, - 851, - 852, - 853, - 854, - 855, - 856, - 857, - 858, - 859, - 860, - 861, - 862, - 863, - 864, - 865, - 866, - 867, - 868, - 869, - 870, - 871, - 872, - 873, - 874, - 875, - 876, - 877, - 878, - 879, - 880, - 881, - 882, - 883, - 884, - 885, - 886, - 887, - 888, - 889, - 890, - 891, - 892, - 893, - 894, - 895, - 896, - 897, - 898, - 899, - 900, - 901, - 902, - 903, - 904, - 905, - 906, - 907, - 908, - 909, - 910, - 911, - 912, - 913, - 914, - 915, - 916, - 917, - 918, - 919, - 920, - 921, - 922, - 923, - 924, - 925, - 926, - 927, - 928, - 929, - 930, - 931, - 932, - 933, - 934, - 935, - 936, - 937, - 938, - 939, - 940, - 941, - 942, - 943, - 944, - 945, - 946, - 947, - 948, - 949, - 950, - 951, - 952, - 953, - 954, - 955, - 956, - 957, - 958, - 959, - 960, - 961, - 962, - 963, - 964, - 965, - 966, - 967, - 968, - 969, - 970, - 971, - 972, - 973, - 974, - 975, - 976, - 977, - 978, - 979, - 980, - 981, - 982, - 983, - 984, - 985, - 986, - 987, - 988, - 989, - 990, - 991, - 992, - 993, - 994, - 995, - 996, - 997, - 998, - 999, - 1000, - 1001, - 1002, - 1003, - 1004, - 1005, - 1006, - 1007, - 1008, - 1009, - 1010, - 1011, - 1012, - 1013, - 1014, - 1015, - 1016, - 1017, - 1018, - 1019, - 1020, - 1021, - 1022, - 1023, - 1024, - 1025, - 1026, - 1027, - 1028, - 1029, - 1030, - 1031, - 1032, - 1033, - 1034, - 1035, - 1036, - 1037, - 1038, - 1039, - 1040, - 1041, - 1042, - 1043, - 1044, - 1045, - 1046, - 1047, - 1048, - 1049, - 1050, - 1051, - 1052, - 1053, - 1054, - 1055, - 1056, - 1057, - 1058, - 1059, - 1060, - 1061, - 1062, - 1063, - 1064, - 1065, - 1066, - 1067, - 1068, - 1069, - 1070, - 1071, - 1072, - 1073, - 1074, - 1075, - 1076, - 1077, - 1078, - 1079, - 1080, - 1081, - 1082, - 1083, - 1084, - 1085, - 1086, - 1087, - 1088, - 1089, - 1090, - 1091, - 1092, - 1093, - 1094, - 1095, - 1096, - 1097, - 1098, - 1099, - 1100, - 1101, - 1102, - 1103, - 1104, - 1105, - 1106, - 1107, - 1108, - 1109, - 1110, - 1111, - 1112, - 1113, - 1114, - 1115, - 1116, - 1117, - 1118, - 1119, - 1120, - 1121, - 1122, - 1123, - 1124, - 1125, - 1126, - 1127, - 1128, - 1129, - 1130, - 1131, - 1132, - 1133, - 1134, - 1135, - 1136, - 1137, - 1138, - 1139, - 1140, - 1141, - 1142, - 1143, - 1144, - 1145, - 1146, - 1147, - 1148, - 1149, - 1150, - 1151, - 1152, - 1153, - 1154, - 1155, - 1156, - 1157, - 1158, - 1159, - 1160, - 1161, - 1162, - 1163, - 1164, - 1165, - 1166, - 1167, - 1168, - 1169, - 1170, - 1171, - 1172, - 1173, - 1174, - 1175, - 1176, - 1177, - 1178, - 1179, - 1180, - 1181, - 1182, - 1183, - 1184, - 1185, - 1186, - 1187, - 1188, - 1189, - 1190, - 1191, - 1192, - 1193, - 1194, - 1195, - 1196, - 1197, - 1198, - 1199, - 1200, - 1201, - 1202, - 1203, - 1204, - 1205, - 1206, - 1207, - 1208, - 1209, - 1210, - 1211, - 1212, - 1213, - 1214, - 1215, - 1216, - 1217, - 1218, - 1219, - 1220, - 1221, - 1222, - 1223, - 1224, - 1225, - 1226, - 1227, - 1228, - 1229, - 1230, - 1231, - 1232, - 1233, - 1234, - 1235, - 1236, - 1237, - 1238, - 1239, - 1240, - 1241, - 1242, - 1243, - 1244, - 1245, - 1246, - 1247, - 1248, - 1249, - 1250, - 1251, - 1252, - 1253, - 1254, - 1255, - 1256, - 1257, - 1258, - 1259, - 1260, - 1261, - 1262, - 1263, - 1264, - 1265, - 1266, - 1267, - 1268, - 1269, - 1270, - 1271, - 1272, - 1273, - 1274, - 1275, - 1276, - 1277, - 1278, - 1279, - 1280, - 1281, - 1282, - 1283, - 1284, - 1285, - 1286, - 1287, - 1288, - 1289, - 1290, - 1291, - 1292, - 1293, - 1294, - 1295, - 1296, - 1297, - 1298, - 1299, - 1300, - 1301, - 1302, - 1303, - 1304, - 1305, - 1306, - 1307, - 1308, - 1309, - 1310, - 1311, - 1312, - 1313, - 1314, - 1315, - 1316, - 1317, - 1318, - 1319, - 1320, - 1321, - 1322, - 1323, - 1324, - 1325, - 1326, - 1327, - 1328, - 1329, - 1330, - 1331, - 1332, - 1333, - 1334, - 1335, - 1336, - 1337, - 1338, - 1339, - 1340, - 1341, - 1342, - 1343, - 1344, - 1345, - 1346, - 1347, - 1348, - 1349, - 1350, - 1351, - 1352, - 1353, - 1354, - 1355, - 1356, - 1357, - 1358, - 1359, - 1360, - 1361, - 1362, - 1363, - 1364, - 1365, - 1366, - 1367, - 1368, - 1369, - 1370, - 1371, - 1372, - 1373, - 1374, - 1375, - 1376, - 1377, - 1378, - 1379, - 1380, - 1381, - 1382, - 1383, - 1384, - 1385, - 1386, - 1387, - 1388, - 1389, - 1390, - 1391, - 1392, - 1393, - 1394, - 1395, - 1396, - 1397, - 1398, - 1399, - 1400, - 1401, - 1402, - 1403, - 1404, - 1405, - 1406, - 1407, - 1408, - 1409, - 1410, - 1411, - 1412, - 1413, - 1414, - 1415, - 1416, - 1417, - 1418, - 1419, - 1420, - 1421, - 1422, - 1423, - 1424, - 1425, - 1426, - 1427, - 1428, - 1429, - 1430, - 1431, - 1432, - 1433, - 1434, - 1435, - 1436, - 1437, - 1438, - 1439, - 1440, - 1441, - 1442, - 1443, - 1444, - 1445, - 1446, - 1447, - 1448, - 1449, - 1450, - 1451, - 1452, - 1453, - 1454, - 1455, - 1456, - 1457, - 1458, - 1459, - 1460, - 1461, - 1462, - 1463, - 1464, - 1465, - 1466, - 1467, - 1468, - 1469, - 1470, - 1471, - 1472, - 1473, - 1474, - 1475, - 1476, - 1477, - 1478, - 1479, - 1480, - 1481, - 1482, - 1483, - 1484, - 1485, - 1486, - 1487, - 1488, - 1489, - 1490, - 1491, - 1492, - 1493, - 1494, - 1495, - 1496, - 1497, - 1498, - 1499, - 1500, - 1501, - 1502, - 1503, - 1504, - 1505, - 1506, - 1507, - 1508, - 1509, - 1510, - 1511, - 1512, - 1513, - 1514, - 1515, - 1516, - 1517, - 1518, - 1519, - 1520, - 1521, - 1522, - 1523, - 1524, - 1525, - 1526, - 1527, - 1528, - 1529, - 1530, - 1531, - 1532, - 1533, - 1534, - 1535, - 1536, - 1537, - 1538, - 1539, - 1540, - 1541, - 1542, - 1543, - 1544, - 1545, - 1546, - 1547, - 1548, - 1549, - 1550, - 1551, - 1552, - 1553, - 1554, - 1555, - 1556, - 1557, - 1558, - 1559, - 1560, - 1561, - 1562, - 1563, - 1564, - 1565, - 1566, - 1567, - 1568, - 1569, - 1570, - 1571, - 1572, - 1573, - 1574, - 1575, - 1576, - 1577, - 1578, - 1579, - 1580, - 1581, - 1582, - 1583, - 1584, - 1585, - 1586, - 1587, - 1588, - 1589, - 1590, - 1591, - 1592, - 1593, - 1594, - 1595, - 1596, - 1597, - 1598, - 1599, - 1600, - 1601, - 1602, - 1603, - 1604, - 1605, - 1606, - 1607, - 1608, - 1609, - 1610, - 1611, - 1612, - 1613, - 1614, - 1615, - 1616, - 1617, - 1618, - 1619, - 1620, - 1621, - 1622, - 1623, - 1624, - 1625, - 1626, - 1627, - 1628, - 1629, - 1630, - 1631, - 1632, - 1633, - 1634, - 1635, - 1636, - 1637, - 1638, - 1639, - 1640, - 1641, - 1642, - 1643, - 1644, - 1645, - 1646, - 1647, - 1648, - 1649, - 1650, - 1651, - 1652, - 1653, - 1654, - 1655, - 1656, - 1657, - 1658, - 1659, - 1660, - 1661, - 1662, - 1663, - 1664, - 1665, - 1666, - 1667, - 1668, - 1669, - 1670, - 1671, - 1672, - 1673, - 1674, - 1675, - 1676, - 1677, - 1678, - 1679, - 1680, - 1681, - 1682, - 1683, - 1684, - 1685, - 1686, - 1687, - 1688, - 1689, - 1690, - 1691, - 1692, - 1693, - 1694, - 1695, - 1696, - 1697, - 1698, - 1699, - 1700, - 1701, - 1702, - 1703, - 1704, - 1705, - 1706, - 1707, - 1708, - 1709, - 1710, - 1711, - 1712, - 1713, - 1714, - 1715, - 1716, - 1717, - 1718, - 1719, - 1720, - 1721, - 1722, - 1723, - 1724, - 1725, - 1726, - 1727, - 1728, - 1729, - 1730, - 1731, - 1732, - 1733, - 1734, - 1735, - 1736, - 1737, - 1738, - 1739, - 1740, - 1741, - 1742, - 1743, - 1744, - 1745, - 1746, - 1747, - 1748, - 1749, - 1750, - 1751, - 1752, - 1753, - 1754, - 1755, - 1756, - 1757, - 1758, - 1759, - 1760, - 1761, - 1762, - 1763, - 1764, - 1765, - 1766, - 1767, - 1768, - 1769, - 1770, - 1771, - 1772, - 1773, - 1774, - 1775, - 1776, - 1777, - 1778, - 1779, - 1780, - 1781, - 1782, - 1783, - 1784, - 1785, - 1786, - 1787, - 1788, - 1789, - 1790, - 1791, - 1792, - 1793, - 1794, - 1795, - 1796, - 1797, - 1798, - 1799, - 1800, - 1801, - 1802, - 1803, - 1804, - 1805, - 1806, - 1807, - 1808, - 1809, - 1810, - 1811, - 1812, - 1813, - 1814, - 1815, - 1816, - 1817, - 1818, - 1819, - 1820, - 1821, - 1822, - 1823, - 1824, - 1825, - 1826, - 1827, - 1828, - 1829, - 1830, - 1831, - 1832, - 1833, - 1834, - 1835, - 1836, - 1837, - 1838, - 1839, - 1840, - 1841, - 1842, - 1843, - 1844, - 1845, - 1846, - 1847, - 1848, - 1849, - 1850, - 1851, - 1852, - 1853, - 1854, - 1855, - 1856, - 1857, - 1858, - 1859, - 1860, - 1861, - 1862, - 1863, - 1864, - 1865, - 1866, - 1867, - 1868, - 1869, - 1870, - 1871, - 1872, - 1873, - 1874, - 1875, - 1876, - 1877, - 1878, - 1879, - 1880, - 1881, - 1882, - 1883, - 1884, - 1885, - 1886, - 1887, - 1888, - 1889, - 1890, - 1891, - 1892, - 1893, - 1894, - 1895, - 1896, - 1897, - 1898, - 1899, - 1900, - 1901, - 1902, - 1903, - 1904, - 1905, - 1906, - 1907, - 1908, - 1909, - 1910, - 1911, - 1912, - 1913, - 1914, - 1915, - 1916, - 1917, - 1918, - 1919, - 1920, - 1921, - 1922, - 1923, - 1924, - 1925, - 1926, - 1927, - 1928, - 1929, - 1930, - 1931, - 1932, - 1933, - 1934, - 1935, - 1936, - 1937, - 1938, - 1939, - 1940, - 1941, - 1942, - 1943, - 1944, - 1945, - 1946, - 1947, - 1948, - 1949, - 1950, - 1951, - 1952, - 1953, - 1954, - 1955, - 1956, - 1957, - 1958, - 1959, - 1960, - 1961, - 1962, - 1963, - 1964, - 1965, - 1966, - 1967, - 1968, - 1969, - 1970, - 1971, - 1972, - 1973, - 1974, - 1975, - 1976, - 1977, - 1978, - 1979, - 1980, - 1981, - 1982, - 1983, - 1984, - 1985, - 1986, - 1987, - 1988, - 1989, - 1990, - 1991, - 1992, - 1993, - 1994, - 1995, - 1996, - 1997, - 1998, - 1999, - 2000, - 2001, - 2002, - 2003, - 2004, - 2005, - 2006, - 2007, - 2008, - 2009, - 2010, - 2011, - 2012, - 2013, - 2014, - 2015, - 2016, - 2017, - 2018, - 2019, - 2020, - 2021, - 2022, - 2023, - 2024, - 2025, - 2026, - 2027, - 2028, - 2029, - 2030, - 2031, - 2032, - 2033, - 2034, - 2035, - 2036, - 2037, - 2038, - 2039, - 2040, - 2041, - 2042, - 2043, - 2044, - 2045, - 2046, - 2047, - 2048, - 2049, - 2050, - 2051, - 2052, - 2053, - 2054, - 2055, - 2056, - 2057, - 2058, - 2059, - 2060, - 2061, - 2062, - 2063, - 2064, - 2065, - 2066, - 2067, - 2068, - 2069, - 2070, - 2071, - 2072, - 2073, - 2074, - 2075, - 2076, - 2077, - 2078, - 2079, - 2080, - 2081, - 2082, - 2083, - 2084, - 2085, - 2086, - 2087, - 2088, - 2089, - 2090, - 2091, - 2092, - 2093, - 2094, - 2095, - 2096, - 2097, - 2098, - 2099, - 2100, - 2101, - 2102, - 2103, - 2104, - 2105, - 2106, - 2107, - 2108, - 2109, - 2110, - 2111, - 2112, - 2113, - 2114, - 2115, - 2116, - 2117, - 2118, - 2119, - 2120, - 2121, - 2122, - 2123, - 2124, - 2125, - 2126, - 2127, - 2128, - 2129, - 2130, - 2131, - 2132, - 2133, - 2134, - 2135, - 2136, - 2137, - 2138, - 2139, - 2140, - 2141, - 2142, - 2143, - 2144, - 2145, - 2146, - 2147, - 2148, - 2149, - 2150, - 2151, - 2152, - 2153, - 2154, - 2155, - 2156, - 2157, - 2158, - 2159, - 2160, - 2161, - 2162, - 2163, - 2164, - 2165, - 2166, - 2167, - 2168, - 2169, - 2170, - 2171, - 2172, - 2173, - 2174, - 2175, - 2176, - 2177, - 2178, - 2179, - 2180, - 2181, - 2182, - 2183, - 2184, - 2185, - 2186, - 2187, - 2188, - 2189, - 2190, - 2191, - 2192, - 2193, - 2194, - 2195, - 2196, - 2197, - 2198, - 2199, - 2200, - 2201, - 2202, - 2203, - 2204, - 2205, - 2206, - 2207, - 2208, - 2209, - 2210, - 2211, - 2212, - 2213, - 2214, - 2215, - 2216, - 2217, - 2218, - 2219, - 2220, - 2221, - 2222, - 2223, - 2224, - 2225, - 2226, - 2227, - 2228, - 2229, - 2230, - 2231, - 2232, - 2233, - 2234, - 2235, - 2236, - 2237, - 2238, - 2239, - 2240, - 2241, - 2242, - 2243, - 2244, - 2245, - 2255, - 2260, - 2265, - 2270, - 2300 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/325_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/325_Buckets.json deleted file mode 100644 index 474fd26cc7bad..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/325_Buckets.json +++ /dev/null @@ -1,6506 +0,0 @@ -{ - "counts": [ - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 3, - 3, - 2, - 2, - 1, - 3, - 3, - 2, - 2, - 1 - ], - "values": [ - 5.5, - 6, - 6.5, - 7, - 7.5, - 8, - 8.5, - 9, - 9.5, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 101, - 102, - 103, - 104, - 105, - 106, - 107, - 108, - 109, - 110, - 111, - 112, - 113, - 114, - 115, - 116, - 117, - 118, - 119, - 120, - 121, - 122, - 123, - 124, - 125, - 126, - 127, - 128, - 129, - 130, - 131, - 132, - 133, - 134, - 135, - 136, - 137, - 138, - 139, - 140, - 141, - 142, - 143, - 144, - 145, - 146, - 147, - 148, - 149, - 150, - 151, - 152, - 153, - 154, - 155, - 156, - 157, - 158, - 159, - 160, - 161, - 162, - 163, - 164, - 165, - 166, - 167, - 168, - 169, - 170, - 171, - 172, - 173, - 174, - 175, - 176, - 177, - 178, - 179, - 180, - 181, - 182, - 183, - 184, - 185, - 186, - 187, - 188, - 189, - 190, - 191, - 192, - 193, - 194, - 195, - 196, - 197, - 198, - 199, - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 209, - 210, - 211, - 212, - 213, - 214, - 215, - 216, - 217, - 218, - 219, - 220, - 221, - 222, - 223, - 224, - 225, - 226, - 227, - 228, - 229, - 230, - 231, - 232, - 233, - 234, - 235, - 236, - 237, - 238, - 239, - 240, - 241, - 242, - 243, - 244, - 245, - 246, - 247, - 248, - 249, - 250, - 251, - 252, - 253, - 254, - 255, - 256, - 257, - 258, - 259, - 260, - 261, - 262, - 263, - 264, - 265, - 266, - 267, - 268, - 269, - 270, - 271, - 272, - 273, - 274, - 275, - 276, - 277, - 278, - 279, - 280, - 281, - 282, - 283, - 284, - 285, - 286, - 287, - 288, - 289, - 290, - 291, - 292, - 293, - 294, - 295, - 296, - 297, - 298, - 299, - 300, - 301, - 302, - 303, - 304, - 305, - 306, - 307, - 308, - 309, - 310, - 311, - 312, - 313, - 314, - 315, - 316, - 317, - 318, - 319, - 320, - 321, - 322, - 323, - 324, - 325, - 326, - 327, - 328, - 329, - 330, - 331, - 332, - 333, - 334, - 335, - 336, - 337, - 338, - 339, - 340, - 341, - 342, - 343, - 344, - 345, - 346, - 347, - 348, - 349, - 350, - 351, - 352, - 353, - 354, - 355, - 356, - 357, - 358, - 359, - 360, - 361, - 362, - 363, - 364, - 365, - 366, - 367, - 368, - 369, - 370, - 371, - 372, - 373, - 374, - 375, - 376, - 377, - 378, - 379, - 380, - 381, - 382, - 383, - 384, - 385, - 386, - 387, - 388, - 389, - 390, - 391, - 392, - 393, - 394, - 395, - 396, - 397, - 398, - 399, - 400, - 401, - 402, - 403, - 404, - 405, - 406, - 407, - 408, - 409, - 410, - 411, - 412, - 413, - 414, - 415, - 416, - 417, - 418, - 419, - 420, - 421, - 422, - 423, - 424, - 425, - 426, - 427, - 428, - 429, - 430, - 431, - 432, - 433, - 434, - 435, - 436, - 437, - 438, - 439, - 440, - 441, - 442, - 443, - 444, - 445, - 446, - 447, - 448, - 449, - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 466, - 467, - 468, - 469, - 470, - 471, - 472, - 473, - 474, - 475, - 476, - 477, - 478, - 479, - 480, - 481, - 482, - 483, - 484, - 485, - 486, - 487, - 488, - 489, - 490, - 491, - 492, - 493, - 494, - 495, - 496, - 497, - 498, - 499, - 500, - 501, - 502, - 503, - 504, - 505, - 506, - 507, - 508, - 509, - 510, - 511, - 512, - 513, - 514, - 515, - 516, - 517, - 518, - 519, - 520, - 521, - 522, - 523, - 524, - 525, - 526, - 527, - 528, - 529, - 530, - 531, - 532, - 533, - 534, - 535, - 536, - 537, - 538, - 539, - 540, - 541, - 542, - 543, - 544, - 545, - 546, - 547, - 548, - 549, - 550, - 551, - 552, - 553, - 554, - 555, - 556, - 557, - 558, - 559, - 560, - 561, - 562, - 563, - 564, - 565, - 566, - 567, - 568, - 569, - 570, - 571, - 572, - 573, - 574, - 575, - 576, - 577, - 578, - 579, - 580, - 581, - 582, - 583, - 584, - 585, - 586, - 587, - 588, - 589, - 590, - 591, - 592, - 593, - 594, - 595, - 596, - 597, - 598, - 599, - 600, - 601, - 602, - 603, - 604, - 605, - 606, - 607, - 608, - 609, - 610, - 611, - 612, - 613, - 614, - 615, - 616, - 617, - 618, - 619, - 620, - 621, - 622, - 623, - 624, - 625, - 626, - 627, - 628, - 629, - 630, - 631, - 632, - 633, - 634, - 635, - 636, - 637, - 638, - 639, - 640, - 641, - 642, - 643, - 644, - 645, - 646, - 647, - 648, - 649, - 650, - 651, - 652, - 653, - 654, - 655, - 656, - 657, - 658, - 659, - 660, - 661, - 662, - 663, - 664, - 665, - 666, - 667, - 668, - 669, - 670, - 671, - 672, - 673, - 674, - 675, - 676, - 677, - 678, - 679, - 680, - 681, - 682, - 683, - 684, - 685, - 686, - 687, - 688, - 689, - 690, - 691, - 692, - 693, - 694, - 695, - 696, - 697, - 698, - 699, - 700, - 701, - 702, - 703, - 704, - 705, - 706, - 707, - 708, - 709, - 710, - 711, - 712, - 713, - 714, - 715, - 716, - 717, - 718, - 719, - 720, - 721, - 722, - 723, - 724, - 725, - 726, - 727, - 728, - 729, - 730, - 731, - 732, - 733, - 734, - 735, - 736, - 737, - 738, - 739, - 740, - 741, - 742, - 743, - 744, - 745, - 746, - 747, - 748, - 749, - 750, - 751, - 752, - 753, - 754, - 755, - 756, - 757, - 758, - 759, - 760, - 761, - 762, - 763, - 764, - 765, - 766, - 767, - 768, - 769, - 770, - 771, - 772, - 773, - 774, - 775, - 776, - 777, - 778, - 779, - 780, - 781, - 782, - 783, - 784, - 785, - 786, - 787, - 788, - 789, - 790, - 791, - 792, - 793, - 794, - 795, - 796, - 797, - 798, - 799, - 800, - 801, - 802, - 803, - 804, - 805, - 806, - 807, - 808, - 809, - 810, - 811, - 812, - 813, - 814, - 815, - 816, - 817, - 818, - 819, - 820, - 821, - 822, - 823, - 824, - 825, - 826, - 827, - 828, - 829, - 830, - 831, - 832, - 833, - 834, - 835, - 836, - 837, - 838, - 839, - 840, - 841, - 842, - 843, - 844, - 845, - 846, - 847, - 848, - 849, - 850, - 851, - 852, - 853, - 854, - 855, - 856, - 857, - 858, - 859, - 860, - 861, - 862, - 863, - 864, - 865, - 866, - 867, - 868, - 869, - 870, - 871, - 872, - 873, - 874, - 875, - 876, - 877, - 878, - 879, - 880, - 881, - 882, - 883, - 884, - 885, - 886, - 887, - 888, - 889, - 890, - 891, - 892, - 893, - 894, - 895, - 896, - 897, - 898, - 899, - 900, - 901, - 902, - 903, - 904, - 905, - 906, - 907, - 908, - 909, - 910, - 911, - 912, - 913, - 914, - 915, - 916, - 917, - 918, - 919, - 920, - 921, - 922, - 923, - 924, - 925, - 926, - 927, - 928, - 929, - 930, - 931, - 932, - 933, - 934, - 935, - 936, - 937, - 938, - 939, - 940, - 941, - 942, - 943, - 944, - 945, - 946, - 947, - 948, - 949, - 950, - 951, - 952, - 953, - 954, - 955, - 956, - 957, - 958, - 959, - 960, - 961, - 962, - 963, - 964, - 965, - 966, - 967, - 968, - 969, - 970, - 971, - 972, - 973, - 974, - 975, - 976, - 977, - 978, - 979, - 980, - 981, - 982, - 983, - 984, - 985, - 986, - 987, - 988, - 989, - 990, - 991, - 992, - 993, - 994, - 995, - 996, - 997, - 998, - 999, - 1000, - 1001, - 1002, - 1003, - 1004, - 1005, - 1006, - 1007, - 1008, - 1009, - 1010, - 1011, - 1012, - 1013, - 1014, - 1015, - 1016, - 1017, - 1018, - 1019, - 1020, - 1021, - 1022, - 1023, - 1024, - 1025, - 1026, - 1027, - 1028, - 1029, - 1030, - 1031, - 1032, - 1033, - 1034, - 1035, - 1036, - 1037, - 1038, - 1039, - 1040, - 1041, - 1042, - 1043, - 1044, - 1045, - 1046, - 1047, - 1048, - 1049, - 1050, - 1051, - 1052, - 1053, - 1054, - 1055, - 1056, - 1057, - 1058, - 1059, - 1060, - 1061, - 1062, - 1063, - 1064, - 1065, - 1066, - 1067, - 1068, - 1069, - 1070, - 1071, - 1072, - 1073, - 1074, - 1075, - 1076, - 1077, - 1078, - 1079, - 1080, - 1081, - 1082, - 1083, - 1084, - 1085, - 1086, - 1087, - 1088, - 1089, - 1090, - 1091, - 1092, - 1093, - 1094, - 1095, - 1096, - 1097, - 1098, - 1099, - 1100, - 1101, - 1102, - 1103, - 1104, - 1105, - 1106, - 1107, - 1108, - 1109, - 1110, - 1111, - 1112, - 1113, - 1114, - 1115, - 1116, - 1117, - 1118, - 1119, - 1120, - 1121, - 1122, - 1123, - 1124, - 1125, - 1126, - 1127, - 1128, - 1129, - 1130, - 1131, - 1132, - 1133, - 1134, - 1135, - 1136, - 1137, - 1138, - 1139, - 1140, - 1141, - 1142, - 1143, - 1144, - 1145, - 1146, - 1147, - 1148, - 1149, - 1150, - 1151, - 1152, - 1153, - 1154, - 1155, - 1156, - 1157, - 1158, - 1159, - 1160, - 1161, - 1162, - 1163, - 1164, - 1165, - 1166, - 1167, - 1168, - 1169, - 1170, - 1171, - 1172, - 1173, - 1174, - 1175, - 1176, - 1177, - 1178, - 1179, - 1180, - 1181, - 1182, - 1183, - 1184, - 1185, - 1186, - 1187, - 1188, - 1189, - 1190, - 1191, - 1192, - 1193, - 1194, - 1195, - 1196, - 1197, - 1198, - 1199, - 1200, - 1201, - 1202, - 1203, - 1204, - 1205, - 1206, - 1207, - 1208, - 1209, - 1210, - 1211, - 1212, - 1213, - 1214, - 1215, - 1216, - 1217, - 1218, - 1219, - 1220, - 1221, - 1222, - 1223, - 1224, - 1225, - 1226, - 1227, - 1228, - 1229, - 1230, - 1231, - 1232, - 1233, - 1234, - 1235, - 1236, - 1237, - 1238, - 1239, - 1240, - 1241, - 1242, - 1243, - 1244, - 1245, - 1246, - 1247, - 1248, - 1249, - 1250, - 1251, - 1252, - 1253, - 1254, - 1255, - 1256, - 1257, - 1258, - 1259, - 1260, - 1261, - 1262, - 1263, - 1264, - 1265, - 1266, - 1267, - 1268, - 1269, - 1270, - 1271, - 1272, - 1273, - 1274, - 1275, - 1276, - 1277, - 1278, - 1279, - 1280, - 1281, - 1282, - 1283, - 1284, - 1285, - 1286, - 1287, - 1288, - 1289, - 1290, - 1291, - 1292, - 1293, - 1294, - 1295, - 1296, - 1297, - 1298, - 1299, - 1300, - 1301, - 1302, - 1303, - 1304, - 1305, - 1306, - 1307, - 1308, - 1309, - 1310, - 1311, - 1312, - 1313, - 1314, - 1315, - 1316, - 1317, - 1318, - 1319, - 1320, - 1321, - 1322, - 1323, - 1324, - 1325, - 1326, - 1327, - 1328, - 1329, - 1330, - 1331, - 1332, - 1333, - 1334, - 1335, - 1336, - 1337, - 1338, - 1339, - 1340, - 1341, - 1342, - 1343, - 1344, - 1345, - 1346, - 1347, - 1348, - 1349, - 1350, - 1351, - 1352, - 1353, - 1354, - 1355, - 1356, - 1357, - 1358, - 1359, - 1360, - 1361, - 1362, - 1363, - 1364, - 1365, - 1366, - 1367, - 1368, - 1369, - 1370, - 1371, - 1372, - 1373, - 1374, - 1375, - 1376, - 1377, - 1378, - 1379, - 1380, - 1381, - 1382, - 1383, - 1384, - 1385, - 1386, - 1387, - 1388, - 1389, - 1390, - 1391, - 1392, - 1393, - 1394, - 1395, - 1396, - 1397, - 1398, - 1399, - 1400, - 1401, - 1402, - 1403, - 1404, - 1405, - 1406, - 1407, - 1408, - 1409, - 1410, - 1411, - 1412, - 1413, - 1414, - 1415, - 1416, - 1417, - 1418, - 1419, - 1420, - 1421, - 1422, - 1423, - 1424, - 1425, - 1426, - 1427, - 1428, - 1429, - 1430, - 1431, - 1432, - 1433, - 1434, - 1435, - 1436, - 1437, - 1438, - 1439, - 1440, - 1441, - 1442, - 1443, - 1444, - 1445, - 1446, - 1447, - 1448, - 1449, - 1450, - 1451, - 1452, - 1453, - 1454, - 1455, - 1456, - 1457, - 1458, - 1459, - 1460, - 1461, - 1462, - 1463, - 1464, - 1465, - 1466, - 1467, - 1468, - 1469, - 1470, - 1471, - 1472, - 1473, - 1474, - 1475, - 1476, - 1477, - 1478, - 1479, - 1480, - 1481, - 1482, - 1483, - 1484, - 1485, - 1486, - 1487, - 1488, - 1489, - 1490, - 1491, - 1492, - 1493, - 1494, - 1495, - 1496, - 1497, - 1498, - 1499, - 1500, - 1501, - 1502, - 1503, - 1504, - 1505, - 1506, - 1507, - 1508, - 1509, - 1510, - 1511, - 1512, - 1513, - 1514, - 1515, - 1516, - 1517, - 1518, - 1519, - 1520, - 1521, - 1522, - 1523, - 1524, - 1525, - 1526, - 1527, - 1528, - 1529, - 1530, - 1531, - 1532, - 1533, - 1534, - 1535, - 1536, - 1537, - 1538, - 1539, - 1540, - 1541, - 1542, - 1543, - 1544, - 1545, - 1546, - 1547, - 1548, - 1549, - 1550, - 1551, - 1552, - 1553, - 1554, - 1555, - 1556, - 1557, - 1558, - 1559, - 1560, - 1561, - 1562, - 1563, - 1564, - 1565, - 1566, - 1567, - 1568, - 1569, - 1570, - 1571, - 1572, - 1573, - 1574, - 1575, - 1576, - 1577, - 1578, - 1579, - 1580, - 1581, - 1582, - 1583, - 1584, - 1585, - 1586, - 1587, - 1588, - 1589, - 1590, - 1591, - 1592, - 1593, - 1594, - 1595, - 1596, - 1597, - 1598, - 1599, - 1600, - 1601, - 1602, - 1603, - 1604, - 1605, - 1606, - 1607, - 1608, - 1609, - 1610, - 1611, - 1612, - 1613, - 1614, - 1615, - 1616, - 1617, - 1618, - 1619, - 1620, - 1621, - 1622, - 1623, - 1624, - 1625, - 1626, - 1627, - 1628, - 1629, - 1630, - 1631, - 1632, - 1633, - 1634, - 1635, - 1636, - 1637, - 1638, - 1639, - 1640, - 1641, - 1642, - 1643, - 1644, - 1645, - 1646, - 1647, - 1648, - 1649, - 1650, - 1651, - 1652, - 1653, - 1654, - 1655, - 1656, - 1657, - 1658, - 1659, - 1660, - 1661, - 1662, - 1663, - 1664, - 1665, - 1666, - 1667, - 1668, - 1669, - 1670, - 1671, - 1672, - 1673, - 1674, - 1675, - 1676, - 1677, - 1678, - 1679, - 1680, - 1681, - 1682, - 1683, - 1684, - 1685, - 1686, - 1687, - 1688, - 1689, - 1690, - 1691, - 1692, - 1693, - 1694, - 1695, - 1696, - 1697, - 1698, - 1699, - 1700, - 1701, - 1702, - 1703, - 1704, - 1705, - 1706, - 1707, - 1708, - 1709, - 1710, - 1711, - 1712, - 1713, - 1714, - 1715, - 1716, - 1717, - 1718, - 1719, - 1720, - 1721, - 1722, - 1723, - 1724, - 1725, - 1726, - 1727, - 1728, - 1729, - 1730, - 1731, - 1732, - 1733, - 1734, - 1735, - 1736, - 1737, - 1738, - 1739, - 1740, - 1741, - 1742, - 1743, - 1744, - 1745, - 1746, - 1747, - 1748, - 1749, - 1750, - 1751, - 1752, - 1753, - 1754, - 1755, - 1756, - 1757, - 1758, - 1759, - 1760, - 1761, - 1762, - 1763, - 1764, - 1765, - 1766, - 1767, - 1768, - 1769, - 1770, - 1771, - 1772, - 1773, - 1774, - 1775, - 1776, - 1777, - 1778, - 1779, - 1780, - 1781, - 1782, - 1783, - 1784, - 1785, - 1786, - 1787, - 1788, - 1789, - 1790, - 1791, - 1792, - 1793, - 1794, - 1795, - 1796, - 1797, - 1798, - 1799, - 1800, - 1801, - 1802, - 1803, - 1804, - 1805, - 1806, - 1807, - 1808, - 1809, - 1810, - 1811, - 1812, - 1813, - 1814, - 1815, - 1816, - 1817, - 1818, - 1819, - 1820, - 1821, - 1822, - 1823, - 1824, - 1825, - 1826, - 1827, - 1828, - 1829, - 1830, - 1831, - 1832, - 1833, - 1834, - 1835, - 1836, - 1837, - 1838, - 1839, - 1840, - 1841, - 1842, - 1843, - 1844, - 1845, - 1846, - 1847, - 1848, - 1849, - 1850, - 1851, - 1852, - 1853, - 1854, - 1855, - 1856, - 1857, - 1858, - 1859, - 1860, - 1861, - 1862, - 1863, - 1864, - 1865, - 1866, - 1867, - 1868, - 1869, - 1870, - 1871, - 1872, - 1873, - 1874, - 1875, - 1876, - 1877, - 1878, - 1879, - 1880, - 1881, - 1882, - 1883, - 1884, - 1885, - 1886, - 1887, - 1888, - 1889, - 1890, - 1891, - 1892, - 1893, - 1894, - 1895, - 1896, - 1897, - 1898, - 1899, - 1900, - 1901, - 1902, - 1903, - 1904, - 1905, - 1906, - 1907, - 1908, - 1909, - 1910, - 1911, - 1912, - 1913, - 1914, - 1915, - 1916, - 1917, - 1918, - 1919, - 1920, - 1921, - 1922, - 1923, - 1924, - 1925, - 1926, - 1927, - 1928, - 1929, - 1930, - 1931, - 1932, - 1933, - 1934, - 1935, - 1936, - 1937, - 1938, - 1939, - 1940, - 1941, - 1942, - 1943, - 1944, - 1945, - 1946, - 1947, - 1948, - 1949, - 1950, - 1951, - 1952, - 1953, - 1954, - 1955, - 1956, - 1957, - 1958, - 1959, - 1960, - 1961, - 1962, - 1963, - 1964, - 1965, - 1966, - 1967, - 1968, - 1969, - 1970, - 1971, - 1972, - 1973, - 1974, - 1975, - 1976, - 1977, - 1978, - 1979, - 1980, - 1981, - 1982, - 1983, - 1984, - 1985, - 1986, - 1987, - 1988, - 1989, - 1990, - 1991, - 1992, - 1993, - 1994, - 1995, - 1996, - 1997, - 1998, - 1999, - 2000, - 2001, - 2002, - 2003, - 2004, - 2005, - 2006, - 2007, - 2008, - 2009, - 2010, - 2011, - 2012, - 2013, - 2014, - 2015, - 2016, - 2017, - 2018, - 2019, - 2020, - 2021, - 2022, - 2023, - 2024, - 2025, - 2026, - 2027, - 2028, - 2029, - 2030, - 2031, - 2032, - 2033, - 2034, - 2035, - 2036, - 2037, - 2038, - 2039, - 2040, - 2041, - 2042, - 2043, - 2044, - 2045, - 2046, - 2047, - 2048, - 2049, - 2050, - 2051, - 2052, - 2053, - 2054, - 2055, - 2056, - 2057, - 2058, - 2059, - 2060, - 2061, - 2062, - 2063, - 2064, - 2065, - 2066, - 2067, - 2068, - 2069, - 2070, - 2071, - 2072, - 2073, - 2074, - 2075, - 2076, - 2077, - 2078, - 2079, - 2080, - 2081, - 2082, - 2083, - 2084, - 2085, - 2086, - 2087, - 2088, - 2089, - 2090, - 2091, - 2092, - 2093, - 2094, - 2095, - 2096, - 2097, - 2098, - 2099, - 2100, - 2101, - 2102, - 2103, - 2104, - 2105, - 2106, - 2107, - 2108, - 2109, - 2110, - 2111, - 2112, - 2113, - 2114, - 2115, - 2116, - 2117, - 2118, - 2119, - 2120, - 2121, - 2122, - 2123, - 2124, - 2125, - 2126, - 2127, - 2128, - 2129, - 2130, - 2131, - 2132, - 2133, - 2134, - 2135, - 2136, - 2137, - 2138, - 2139, - 2140, - 2141, - 2142, - 2143, - 2144, - 2145, - 2146, - 2147, - 2148, - 2149, - 2150, - 2151, - 2152, - 2153, - 2154, - 2155, - 2156, - 2157, - 2158, - 2159, - 2160, - 2161, - 2162, - 2163, - 2164, - 2165, - 2166, - 2167, - 2168, - 2169, - 2170, - 2171, - 2172, - 2173, - 2174, - 2175, - 2176, - 2177, - 2178, - 2179, - 2180, - 2181, - 2182, - 2183, - 2184, - 2185, - 2186, - 2187, - 2188, - 2189, - 2190, - 2191, - 2192, - 2193, - 2194, - 2195, - 2196, - 2197, - 2198, - 2199, - 2200, - 2201, - 2202, - 2203, - 2204, - 2205, - 2206, - 2207, - 2208, - 2209, - 2210, - 2211, - 2212, - 2213, - 2214, - 2215, - 2216, - 2217, - 2218, - 2219, - 2220, - 2221, - 2222, - 2223, - 2224, - 2225, - 2226, - 2227, - 2228, - 2229, - 2230, - 2231, - 2232, - 2233, - 2234, - 2235, - 2236, - 2237, - 2238, - 2239, - 2240, - 2241, - 2242, - 2243, - 2244, - 2245, - 2246, - 2247, - 2248, - 2249, - 2250, - 2251, - 2252, - 2253, - 2254, - 2255, - 2256, - 2257, - 2258, - 2259, - 2260, - 2261, - 2262, - 2263, - 2264, - 2265, - 2266, - 2267, - 2268, - 2269, - 2270, - 2271, - 2272, - 2273, - 2274, - 2275, - 2276, - 2277, - 2278, - 2279, - 2280, - 2281, - 2282, - 2283, - 2284, - 2285, - 2286, - 2287, - 2288, - 2289, - 2290, - 2291, - 2292, - 2293, - 2294, - 2295, - 2296, - 2297, - 2298, - 2299, - 2300, - 2301, - 2302, - 2303, - 2304, - 2305, - 2306, - 2307, - 2308, - 2309, - 2310, - 2311, - 2312, - 2313, - 2314, - 2315, - 2316, - 2317, - 2318, - 2319, - 2320, - 2321, - 2322, - 2323, - 2324, - 2325, - 2326, - 2327, - 2328, - 2329, - 2330, - 2331, - 2332, - 2333, - 2334, - 2335, - 2336, - 2337, - 2338, - 2339, - 2340, - 2341, - 2342, - 2343, - 2344, - 2345, - 2346, - 2347, - 2348, - 2349, - 2350, - 2351, - 2352, - 2353, - 2354, - 2355, - 2356, - 2357, - 2358, - 2359, - 2360, - 2361, - 2362, - 2363, - 2364, - 2365, - 2366, - 2367, - 2368, - 2369, - 2370, - 2371, - 2372, - 2373, - 2374, - 2375, - 2376, - 2377, - 2378, - 2379, - 2380, - 2381, - 2382, - 2383, - 2384, - 2385, - 2386, - 2387, - 2388, - 2389, - 2390, - 2391, - 2392, - 2393, - 2394, - 2395, - 2396, - 2397, - 2398, - 2399, - 2400, - 2401, - 2402, - 2403, - 2404, - 2405, - 2406, - 2407, - 2408, - 2409, - 2410, - 2411, - 2412, - 2413, - 2414, - 2415, - 2416, - 2417, - 2418, - 2419, - 2420, - 2421, - 2422, - 2423, - 2424, - 2425, - 2426, - 2427, - 2428, - 2429, - 2430, - 2431, - 2432, - 2433, - 2434, - 2435, - 2436, - 2437, - 2438, - 2439, - 2440, - 2441, - 2442, - 2443, - 2444, - 2445, - 2446, - 2447, - 2448, - 2449, - 2450, - 2451, - 2452, - 2453, - 2454, - 2455, - 2456, - 2457, - 2458, - 2459, - 2460, - 2461, - 2462, - 2463, - 2464, - 2465, - 2466, - 2467, - 2468, - 2469, - 2470, - 2471, - 2472, - 2473, - 2474, - 2475, - 2476, - 2477, - 2478, - 2479, - 2480, - 2481, - 2482, - 2483, - 2484, - 2485, - 2486, - 2487, - 2488, - 2489, - 2490, - 2491, - 2492, - 2493, - 2494, - 2495, - 2496, - 2497, - 2498, - 2499, - 2500, - 2501, - 2502, - 2503, - 2504, - 2505, - 2506, - 2507, - 2508, - 2509, - 2510, - 2511, - 2512, - 2513, - 2514, - 2515, - 2516, - 2517, - 2518, - 2519, - 2520, - 2521, - 2522, - 2523, - 2524, - 2525, - 2526, - 2527, - 2528, - 2529, - 2530, - 2531, - 2532, - 2533, - 2534, - 2535, - 2536, - 2537, - 2538, - 2539, - 2540, - 2541, - 2542, - 2543, - 2544, - 2545, - 2546, - 2547, - 2548, - 2549, - 2550, - 2551, - 2552, - 2553, - 2554, - 2555, - 2556, - 2557, - 2558, - 2559, - 2560, - 2561, - 2562, - 2563, - 2564, - 2565, - 2566, - 2567, - 2568, - 2569, - 2570, - 2571, - 2572, - 2573, - 2574, - 2575, - 2576, - 2577, - 2578, - 2579, - 2580, - 2581, - 2582, - 2583, - 2584, - 2585, - 2586, - 2587, - 2588, - 2589, - 2590, - 2591, - 2592, - 2593, - 2594, - 2595, - 2596, - 2597, - 2598, - 2599, - 2600, - 2601, - 2602, - 2603, - 2604, - 2605, - 2606, - 2607, - 2608, - 2609, - 2610, - 2611, - 2612, - 2613, - 2614, - 2615, - 2616, - 2617, - 2618, - 2619, - 2620, - 2621, - 2622, - 2623, - 2624, - 2625, - 2626, - 2627, - 2628, - 2629, - 2630, - 2631, - 2632, - 2633, - 2634, - 2635, - 2636, - 2637, - 2638, - 2639, - 2640, - 2641, - 2642, - 2643, - 2644, - 2645, - 2646, - 2647, - 2648, - 2649, - 2650, - 2651, - 2652, - 2653, - 2654, - 2655, - 2656, - 2657, - 2658, - 2659, - 2660, - 2661, - 2662, - 2663, - 2664, - 2665, - 2666, - 2667, - 2668, - 2669, - 2670, - 2671, - 2672, - 2673, - 2674, - 2675, - 2676, - 2677, - 2678, - 2679, - 2680, - 2681, - 2682, - 2683, - 2684, - 2685, - 2686, - 2687, - 2688, - 2689, - 2690, - 2691, - 2692, - 2693, - 2694, - 2695, - 2696, - 2697, - 2698, - 2699, - 2700, - 2701, - 2702, - 2703, - 2704, - 2705, - 2706, - 2707, - 2708, - 2709, - 2710, - 2711, - 2712, - 2713, - 2714, - 2715, - 2716, - 2717, - 2718, - 2719, - 2720, - 2721, - 2722, - 2723, - 2724, - 2725, - 2726, - 2727, - 2728, - 2729, - 2730, - 2731, - 2732, - 2733, - 2734, - 2735, - 2736, - 2737, - 2738, - 2739, - 2740, - 2741, - 2742, - 2743, - 2744, - 2745, - 2746, - 2747, - 2748, - 2749, - 2750, - 2751, - 2752, - 2753, - 2754, - 2755, - 2756, - 2757, - 2758, - 2759, - 2760, - 2761, - 2762, - 2763, - 2764, - 2765, - 2766, - 2767, - 2768, - 2769, - 2770, - 2771, - 2772, - 2773, - 2774, - 2775, - 2776, - 2777, - 2778, - 2779, - 2780, - 2781, - 2782, - 2783, - 2784, - 2785, - 2786, - 2787, - 2788, - 2789, - 2790, - 2791, - 2792, - 2793, - 2794, - 2795, - 2796, - 2797, - 2798, - 2799, - 2800, - 2801, - 2802, - 2803, - 2804, - 2805, - 2806, - 2807, - 2808, - 2809, - 2810, - 2811, - 2812, - 2813, - 2814, - 2815, - 2816, - 2817, - 2818, - 2819, - 2820, - 2821, - 2822, - 2823, - 2824, - 2825, - 2826, - 2827, - 2828, - 2829, - 2830, - 2831, - 2832, - 2833, - 2834, - 2835, - 2836, - 2837, - 2838, - 2839, - 2840, - 2841, - 2842, - 2843, - 2844, - 2845, - 2846, - 2847, - 2848, - 2849, - 2850, - 2851, - 2852, - 2853, - 2854, - 2855, - 2856, - 2857, - 2858, - 2859, - 2860, - 2861, - 2862, - 2863, - 2864, - 2865, - 2866, - 2867, - 2868, - 2869, - 2870, - 2871, - 2872, - 2873, - 2874, - 2875, - 2876, - 2877, - 2878, - 2879, - 2880, - 2881, - 2882, - 2883, - 2884, - 2885, - 2886, - 2887, - 2888, - 2889, - 2890, - 2891, - 2892, - 2893, - 2894, - 2895, - 2896, - 2897, - 2898, - 2899, - 2900, - 2901, - 2902, - 2903, - 2904, - 2905, - 2906, - 2907, - 2908, - 2909, - 2910, - 2911, - 2912, - 2913, - 2914, - 2915, - 2916, - 2917, - 2918, - 2919, - 2920, - 2921, - 2922, - 2923, - 2924, - 2925, - 2926, - 2927, - 2928, - 2929, - 2930, - 2931, - 2932, - 2933, - 2934, - 2935, - 2936, - 2937, - 2938, - 2939, - 2940, - 2941, - 2942, - 2943, - 2944, - 2945, - 2946, - 2947, - 2948, - 2949, - 2950, - 2951, - 2952, - 2953, - 2954, - 2955, - 2956, - 2957, - 2958, - 2959, - 2960, - 2961, - 2962, - 2963, - 2964, - 2965, - 2966, - 2967, - 2968, - 2969, - 2970, - 2971, - 2972, - 2973, - 2974, - 2975, - 2976, - 2977, - 2978, - 2979, - 2980, - 2981, - 2982, - 2983, - 2984, - 2985, - 2986, - 2987, - 2988, - 2989, - 2990, - 2991, - 2992, - 2993, - 2994, - 2995, - 2996, - 2997, - 2998, - 2999, - 3000, - 3001, - 3002, - 3003, - 3004, - 3005, - 3006, - 3007, - 3008, - 3009, - 3010, - 3011, - 3012, - 3013, - 3014, - 3015, - 3016, - 3017, - 3018, - 3019, - 3020, - 3021, - 3022, - 3023, - 3024, - 3025, - 3026, - 3027, - 3028, - 3029, - 3030, - 3031, - 3032, - 3033, - 3034, - 3035, - 3036, - 3037, - 3038, - 3039, - 3040, - 3041, - 3042, - 3043, - 3044, - 3045, - 3046, - 3047, - 3048, - 3049, - 3050, - 3051, - 3052, - 3053, - 3054, - 3055, - 3056, - 3057, - 3058, - 3059, - 3060, - 3061, - 3062, - 3063, - 3064, - 3065, - 3066, - 3067, - 3068, - 3069, - 3070, - 3071, - 3072, - 3073, - 3074, - 3075, - 3076, - 3077, - 3078, - 3079, - 3080, - 3081, - 3082, - 3083, - 3084, - 3085, - 3086, - 3087, - 3088, - 3089, - 3090, - 3091, - 3092, - 3093, - 3094, - 3095, - 3096, - 3097, - 3098, - 3099, - 3100, - 3101, - 3102, - 3103, - 3104, - 3105, - 3106, - 3107, - 3108, - 3109, - 3110, - 3111, - 3112, - 3113, - 3114, - 3115, - 3116, - 3117, - 3118, - 3119, - 3120, - 3121, - 3122, - 3123, - 3124, - 3125, - 3126, - 3127, - 3128, - 3129, - 3130, - 3131, - 3132, - 3133, - 3134, - 3135, - 3136, - 3137, - 3138, - 3139, - 3140, - 3141, - 3142, - 3143, - 3144, - 3145, - 3146, - 3147, - 3148, - 3149, - 3150, - 3151, - 3152, - 3153, - 3154, - 3155, - 3156, - 3157, - 3158, - 3159, - 3160, - 3161, - 3162, - 3163, - 3164, - 3165, - 3166, - 3167, - 3168, - 3169, - 3170, - 3171, - 3172, - 3173, - 3174, - 3175, - 3176, - 3177, - 3178, - 3179, - 3180, - 3181, - 3182, - 3183, - 3184, - 3185, - 3186, - 3187, - 3188, - 3189, - 3190, - 3191, - 3192, - 3193, - 3194, - 3195, - 3196, - 3197, - 3198, - 3199, - 3200, - 3201, - 3202, - 3203, - 3204, - 3205, - 3206, - 3207, - 3208, - 3209, - 3210, - 3211, - 3212, - 3213, - 3214, - 3215, - 3216, - 3217, - 3218, - 3219, - 3220, - 3221, - 3222, - 3223, - 3224, - 3225, - 3226, - 3227, - 3228, - 3229, - 3230, - 3231, - 3232, - 3233, - 3234, - 3235, - 3236, - 3237, - 3238, - 3239, - 3240, - 3241, - 3242, - 3243, - 3244, - 3245, - 3255, - 3260, - 3265, - 3270, - 3300 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Basic_Histogram.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Basic_Histogram.json deleted file mode 100644 index a4fe5c1ca7f30..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Basic_Histogram.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "counts": [ - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 4, - 4, - 3, - 2, - 2, - 3, - 2, - 2, - 2 - ], - "values": [ - 11.5, - 13, - 14.5, - 16, - 17.5, - 19, - 20.5, - 22, - 23.5, - 25, - 27.5, - 30, - 32.5, - 35, - 37.5, - 40, - 42.5, - 45, - 47.5, - 50, - 52.5, - 55, - 57.5, - 60, - 62.5, - 65, - 67.5, - 70, - 72.5, - 75, - 77.5, - 80, - 82.5, - 85, - 87.5, - 107.14285714285714, - 114.28571428571429, - 121.42857142857143, - 200 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Cumulative_bucket_starts_at_0.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Cumulative_bucket_starts_at_0.json deleted file mode 100644 index 0abcfbc8f56aa..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Cumulative_bucket_starts_at_0.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "counts": [ - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 100, - 100, - 100, - 100, - 100, - 100, - 100, - 100, - 100, - 100, - 208, - 169, - 133, - 102, - 75, - 52, - 33, - 18, - 8, - 2, - 169, - 137, - 109, - 83, - 61, - 42, - 27, - 15, - 6, - 1, - 52, - 43, - 34, - 26, - 19, - 12, - 8, - 4, - 2, - 11, - 9, - 7, - 6, - 4, - 2, - 1, - 6, - 5, - 4, - 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 3, - 2, - 2, - 1, - 0, - 0, - 0, - 2, - 1 - ], - "values": [ - -0.0045000000000000005, - -0.004, - -0.0035, - -0.003, - -0.0025, - -0.002, - -0.0015, - -0.001, - -0.0004999999999999996, - 0, - 0.0005, - 0.001, - 0.0015, - 0.002, - 0.0025, - 0.003, - 0.0035, - 0.004, - 0.0045000000000000005, - 0.005, - 0.006500000000000001, - 0.008, - 0.009500000000000001, - 0.011, - 0.0125, - 0.014000000000000002, - 0.0155, - 0.017, - 0.0185, - 0.02, - 0.028, - 0.036000000000000004, - 0.044, - 0.052000000000000005, - 0.06, - 0.068, - 0.076, - 0.084, - 0.09200000000000001, - 0.1, - 0.11, - 0.12000000000000001, - 0.13, - 0.14, - 0.15000000000000002, - 0.16, - 0.17, - 0.18, - 0.19, - 0.2, - 0.23, - 0.26, - 0.29000000000000004, - 0.32, - 0.35, - 0.38, - 0.41000000000000003, - 0.44, - 0.47000000000000003, - 0.5, - 0.55, - 0.6, - 0.65, - 0.7, - 0.75, - 0.8, - 0.8500000000000001, - 0.9, - 0.95, - 1.1, - 1.2, - 1.3, - 1.4, - 1.5, - 1.6, - 1.7000000000000002, - 2.3, - 2.6, - 2.9, - 3.2, - 3.5, - 3.8, - 5.5, - 6, - 6.5, - 7, - 7.5, - 8, - 8.5, - 9, - 9.5, - 10, - 10.625, - 11.25, - 11.875, - 22.5, - 15 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/First_bucket_boundary_equals_minimum.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/First_bucket_boundary_equals_minimum.json deleted file mode 100644 index c949e90515698..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/First_bucket_boundary_equals_minimum.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "counts": [ - 6, - 5, - 4, - 3, - 1, - 1, - 0, - 0, - 0, - 0, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 7, - 6, - 5, - 3, - 2, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, - 2, - 3, - 4, - 4, - 3, - 3, - 2, - 2 - ], - "values": [ - 9.1, - 9.2, - 9.3, - 9.4, - 9.5, - 9.6, - 9.7, - 9.8, - 9.9, - 10, - 16.5, - 23, - 29.5, - 36, - 42.5, - 49, - 55.5, - 62, - 68.5, - 75, - 77.5, - 80, - 82.5, - 85, - 87.5, - 90, - 92.5, - 95, - 97.5, - 100, - 105, - 110, - 115, - 120, - 125, - 130, - 135, - 140, - 145, - 150, - 151, - 152, - 153, - 160 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Large_Numbers.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Large_Numbers.json deleted file mode 100644 index 31a724ee234c1..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Large_Numbers.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "counts": [ - 53, - 43, - 34, - 26, - 18, - 13, - 8, - 4, - 2, - 79, - 64, - 51, - 38, - 28, - 19, - 12, - 7, - 3, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 24, - 39, - 32, - 25, - 20, - 15, - 9, - 6, - 3, - 1, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 13, - 11, - 9, - 7, - 4, - 3, - 2, - 1 - ], - "values": [ - 190000, - 280000, - 370000, - 460000, - 550000, - 640000, - 730000, - 820000, - 910000, - 1900000, - 2800000, - 3700000, - 4600000, - 5500000, - 6400000, - 7300000, - 8200000, - 9100000, - 14000000, - 18000000, - 22000000, - 26000000, - 30000000, - 34000000, - 38000000, - 42000000, - 46000000, - 50000000, - 55000000, - 60000000, - 65000000, - 70000000, - 75000000, - 80000000, - 85000000, - 90000000, - 95000000, - 140000000, - 180000000, - 220000000, - 260000000, - 300000000, - 340000000, - 380000000, - 420000000, - 460000000, - 500000000, - 550000000, - 600000000, - 650000000, - 700000000, - 750000000, - 800000000, - 850000000, - 1000000000 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Many_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Many_Buckets.json deleted file mode 100644 index f32cbd17e8b81..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Many_Buckets.json +++ /dev/null @@ -1,434 +0,0 @@ -{ - "counts": [ - 14, - 11, - 9, - 7, - 4, - 3, - 2, - 1, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 14, - 11, - 9, - 7, - 4, - 3, - 2, - 1, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 14, - 12, - 9, - 7, - 5, - 3, - 2, - 1 - ], - "values": [ - 0.55, - 0.6, - 0.65, - 0.7, - 0.75, - 0.8, - 0.8500000000000001, - 0.9, - 1.4, - 1.8, - 2.2, - 2.6, - 3, - 3.4000000000000004, - 3.8000000000000003, - 4.2, - 4.6, - 5, - 5.5, - 6, - 6.5, - 7, - 7.5, - 8, - 8.5, - 9, - 9.5, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 110, - 120, - 130, - 140, - 150, - 160, - 170, - 180, - 190, - 200, - 210, - 220, - 230, - 240, - 250, - 260, - 270, - 280, - 290, - 300, - 310, - 320, - 330, - 340, - 350, - 360, - 370, - 380, - 390, - 400, - 410, - 420, - 430, - 440, - 450, - 460, - 470, - 480, - 490, - 500, - 510, - 520, - 530, - 540, - 550, - 560, - 570, - 580, - 590, - 600, - 610, - 620, - 630, - 640, - 650, - 660, - 670, - 680, - 690, - 700, - 710, - 720, - 730, - 740, - 750, - 760, - 770, - 780, - 790, - 800, - 810, - 820, - 830, - 840, - 850, - 860, - 870, - 880, - 890, - 900, - 910, - 920, - 930, - 940, - 950, - 960, - 970, - 980, - 990, - 1000, - 1010, - 1020, - 1030, - 1040, - 1050, - 1060, - 1070, - 1100 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Negative_and_Positive_Boundaries.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Negative_and_Positive_Boundaries.json deleted file mode 100644 index 2ef3dd0f52635..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Negative_and_Positive_Boundaries.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "counts": [ - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 7, - 6, - 5, - 4, - 2, - 1, - 1, - 2, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 7, - 6, - 5, - 3, - 2, - 1, - 1 - ], - "values": [ - -48, - -46, - -44, - -42, - -40, - -38, - -36, - -34, - -32, - -30, - -28, - -26, - -24, - -22, - -20, - -18, - -16, - 6, - 10, - 12, - 14, - 16, - 18, - 20, - 22, - 24, - 26, - 28, - 30, - 32, - 34, - 36, - 38, - 40, - 42, - 50 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_Max_with_Single_Value.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_Max_with_Single_Value.json deleted file mode 100644 index 55b100a9420e5..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_Max_with_Single_Value.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "counts": [ - 1 - ], - "values": [ - 150 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_or_Max.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_or_Max.json deleted file mode 100644 index abaea0a424368..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/No_Min_or_Max.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "counts": [ - 2, - 2, - 2, - 2, - 2, - 1, - 1, - 1, - 1, - 1, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 7, - 6, - 4, - 4, - 2, - 1, - 3, - 3, - 2, - 2, - 3, - 2 - ], - "values": [ - -26, - -22, - -18, - -14, - -10, - -6, - -2, - 2, - 6, - 10, - 14, - 18, - 22, - 26, - 30, - 34, - 38, - 42, - 46, - 50, - 55, - 60, - 65, - 70, - 75, - 80, - 110, - 120, - 130, - 140, - 180, - 160 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Max_Defined.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Max_Defined.json deleted file mode 100644 index dbcc0f0206e63..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Max_Defined.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "counts": [ - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 4, - 4, - 3, - 2, - 2, - 1, - 1, - 1, - 1, - 1, - 3, - 2 - ], - "values": [ - 10, - 20, - 30, - 40, - 50, - 60, - 70, - 80, - 90, - 100, - 110, - 120, - 130, - 140, - 150, - 160, - 170, - 180, - 190, - 200, - 210, - 220, - 230, - 240, - 250, - 260, - 270, - 280, - 290, - 300, - 310, - 320, - 330, - 340, - 350, - 420, - 440, - 460, - 480, - 500, - 550, - 750 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Min_Defined.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Min_Defined.json deleted file mode 100644 index eacb60ae33609..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Min_Defined.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "counts": [ - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 4, - 3, - 3, - 2, - 2, - 3, - 2 - ], - "values": [ - 27.5, - 30, - 32.5, - 35, - 37.5, - 40, - 42.5, - 45, - 47.5, - 50, - 55, - 60, - 65, - 70, - 75, - 80, - 85, - 90, - 95, - 100, - 105, - 110, - 115, - 120, - 125, - 140, - 130 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Negative_Boundaries.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Negative_Boundaries.json deleted file mode 100644 index fb5f79be04224..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Only_Negative_Boundaries.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "counts": [ - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2 - ], - "values": [ - -195, - -190, - -185, - -180, - -175, - -170, - -165, - -160, - -155, - -150, - -145, - -140, - -135, - -130, - -125, - -120, - -115, - -110, - -105, - -100, - -97.5, - -95, - -92.5, - -90, - -87.5, - -85, - -82.5, - -80, - -77.5, - -75, - -72.5, - -70, - -67.5, - -65, - -62.5, - -60, - -57.5, - -55, - -52.5, - -50, - -46.42857142857143, - -42.857142857142854, - -39.285714285714285, - -35.714285714285715, - -32.14285714285714, - -28.57142857142857, - -25, - -10 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Positive_boundaries_but_implied_Negative_Values.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Positive_boundaries_but_implied_Negative_Values.json deleted file mode 100644 index ab61ba86ef1ad..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Positive_boundaries_but_implied_Negative_Values.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "counts": [ - 7, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 3, - 1, - 1 - ], - "values": [ - -90, - -80, - -70, - -60, - -50, - -40, - -30, - -20, - -10, - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 32, - 34, - 36, - 38, - 40, - 42.5, - 45, - 60 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Single_Bucket.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Single_Bucket.json deleted file mode 100644 index e33aad55dcba5..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Single_Bucket.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "counts": [ - 51 - ], - "values": [ - 40 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Tail_Heavy_Histogram.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Tail_Heavy_Histogram.json deleted file mode 100644 index d0a395b791506..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Tail_Heavy_Histogram.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "counts": [ - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 2, - 1, - 1, - 1, - 1, - 1, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 1, - 2, - 3, - 4, - 7, - 9, - 11, - 13, - 2, - 8, - 18, - 33, - 52, - 75, - 102, - 133, - 169, - 208, - 26, - 22, - 17, - 13, - 9, - 6, - 4, - 2, - 1, - 3, - 3, - 2, - 2 - ], - "values": [ - 15, - 20, - 25, - 28.571428571428573, - 32.142857142857146, - 35.714285714285715, - 39.285714285714285, - 42.85714285714286, - 46.42857142857143, - 50, - 52.5, - 55, - 57.5, - 60, - 62.5, - 65, - 67.5, - 70, - 72.5, - 75, - 77.5, - 80, - 82.5, - 85, - 87.5, - 90, - 92.5, - 95, - 97.5, - 100, - 107.5, - 110, - 112.5, - 115, - 117.5, - 120, - 122.5, - 125, - 127.5, - 130, - 132.5, - 135, - 137.5, - 140, - 142.5, - 145, - 147.5, - 150, - 150.1, - 150.2, - 150.3, - 150.4, - 150.5, - 150.6, - 150.7, - 150.8, - 150.9, - 155.9, - 160.8, - 165.7, - 151 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Two_Buckets.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Two_Buckets.json deleted file mode 100644 index 492f791b5b53a..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Two_Buckets.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "counts": [ - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 2, - 2 - ], - "values": [ - 1.4, - 1.8, - 2.2, - 2.6, - 3, - 3.4000000000000004, - 3.8000000000000003, - 4.2, - 4.6, - 5, - 5.5, - 6, - 6.5, - 10 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Unbounded_Histogram.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Unbounded_Histogram.json deleted file mode 100644 index fd58d611c3349..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Unbounded_Histogram.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "counts": [ - 75 - ], - "values": [ - 0 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Very_Small_Numbers.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Very_Small_Numbers.json deleted file mode 100644 index 3ad0c6d1cbaf2..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Very_Small_Numbers.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "counts": [ - 3, - 3, - 2, - 2, - 1, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 4, - 4, - 3, - 2, - 2, - 3, - 1, - 1 - ], - "values": [ - 1.8999999999999998e-8, - 2.8e-8, - 3.7e-8, - 4.6e-8, - 5.5e-8, - 1.8999999999999998e-7, - 2.7999999999999997e-7, - 3.7e-7, - 4.6e-7, - 5.5e-7, - 6.4e-7, - 7.3e-7, - 8.2e-7, - 9.1e-7, - 0.000001, - 0.0000011, - 0.0000012, - 0.0000012999999999999998, - 0.0000014, - 0.0000015, - 0.0000016, - 0.0000016999999999999998, - 0.0000018, - 0.0000019, - 0.000002, - 0.0000021, - 0.0000022, - 0.0000023, - 0.0000024, - 0.0000024999999999999998, - 0.0000026, - 0.0000027, - 0.0000028000000000000003, - 0.0000029, - 0.000003, - 0.0000031, - 0.0000032, - 0.0000032999999999999997, - 0.0000034, - 0.0000035, - 0.00000425, - 0.0000045, - 0.000006 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/Zero_Counts_and_Sparse_Data.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/Zero_Counts_and_Sparse_Data.json deleted file mode 100644 index a10b64f9576bf..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/Zero_Counts_and_Sparse_Data.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "counts": [ - 14, - 11, - 9, - 7, - 4, - 3, - 2, - 1, - 11, - 9, - 7, - 5, - 4, - 2, - 1, - 3, - 3, - 2, - 2, - 1 - ], - "values": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 140, - 180, - 220, - 260, - 300, - 340, - 380, - 1050, - 1100, - 1150, - 1200, - 1500 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/lognormal.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/lognormal.json deleted file mode 100644 index 22483f866c273..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/lognormal.json +++ /dev/null @@ -1,1058 +0,0 @@ -{ - "counts": [ - 45, - 44, - 44, - 44, - 44, - 44, - 44, - 44, - 44, - 44, - 87, - 87, - 87, - 86, - 86, - 86, - 86, - 86, - 86, - 86, - 90, - 90, - 90, - 90, - 90, - 90, - 89, - 89, - 89, - 89, - 77, - 77, - 77, - 77, - 77, - 77, - 77, - 77, - 77, - 76, - 68, - 68, - 68, - 68, - 67, - 67, - 67, - 67, - 67, - 67, - 59, - 59, - 59, - 59, - 59, - 59, - 59, - 59, - 59, - 58, - 51, - 51, - 51, - 51, - 51, - 51, - 50, - 50, - 50, - 50, - 46, - 46, - 46, - 46, - 46, - 46, - 45, - 45, - 45, - 45, - 42, - 42, - 42, - 41, - 41, - 41, - 41, - 41, - 41, - 41, - 35, - 35, - 34, - 34, - 34, - 34, - 34, - 34, - 34, - 34, - 32, - 32, - 32, - 31, - 31, - 31, - 31, - 31, - 31, - 31, - 26, - 26, - 26, - 26, - 26, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 23, - 23, - 23, - 23, - 23, - 23, - 19, - 19, - 19, - 19, - 19, - 19, - 19, - 19, - 19, - 18, - 21, - 21, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 18, - 18, - 17, - 17, - 17, - 17, - 17, - 17, - 17, - 17, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 11, - 11, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 10, - 10, - 10, - 10, - 10, - 10, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 7, - 7, - 7, - 8, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 6, - 6, - 6, - 6, - 6, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 6, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 7, - 7, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 5, - 5, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5, - 5, - 5, - 4, - 4, - 4, - 4, - 5, - 5, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 5, - 5, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 7, - 7, - 7, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 4, - 4, - 4, - 4, - 4, - 4, - 3, - 3, - 3, - 3, - 5, - 5, - 5, - 5, - 5, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 4, - 4, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 1, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 1, - 1, - 1, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 1, - 1, - 1, - 111, - 90, - 71, - 55, - 40, - 28, - 18, - 9, - 4, - 1, - 27, - 22, - 18, - 14, - 9, - 6, - 4, - 2, - 1, - 7, - 6, - 5, - 4, - 2, - 1, - 1 - ], - "values": [ - 0.000145693, - 0.000240616, - 0.000335539, - 0.000430462, - 0.000525385, - 0.000620308, - 0.000715231, - 0.000810154, - 0.000905077, - 0.001, - 0.0011, - 0.0012000000000000001, - 0.0013, - 0.0014, - 0.0015, - 0.0016, - 0.0017000000000000001, - 0.0018, - 0.0019000000000000002, - 0.002, - 0.0021, - 0.0022, - 0.0023, - 0.0024000000000000002, - 0.0025, - 0.0026, - 0.0027, - 0.0028, - 0.0029000000000000002, - 0.003, - 0.0031, - 0.0032, - 0.0033, - 0.0034000000000000002, - 0.0035, - 0.0036, - 0.0037, - 0.0038, - 0.0039000000000000003, - 0.004, - 0.0041, - 0.0042, - 0.0043, - 0.0044, - 0.0045000000000000005, - 0.0046, - 0.0047, - 0.0048000000000000004, - 0.0049, - 0.005, - 0.0051, - 0.0052, - 0.0053, - 0.0054, - 0.0055, - 0.0056, - 0.0057, - 0.0058000000000000005, - 0.0059, - 0.006, - 0.0061, - 0.0062, - 0.0063, - 0.0064, - 0.006500000000000001, - 0.0066, - 0.0067, - 0.0068000000000000005, - 0.0069, - 0.007, - 0.0071, - 0.0072, - 0.0073, - 0.0074, - 0.0075, - 0.0076, - 0.0077, - 0.0078000000000000005, - 0.0079, - 0.008, - 0.0081, - 0.0082, - 0.0083, - 0.0084, - 0.0085, - 0.0086, - 0.0087, - 0.008799999999999999, - 0.0089, - 0.009, - 0.009099999999999999, - 0.0092, - 0.0093, - 0.0094, - 0.0095, - 0.0096, - 0.0097, - 0.0098, - 0.0099, - 0.01, - 0.0101, - 0.0102, - 0.0103, - 0.0104, - 0.010499999999999999, - 0.0106, - 0.0107, - 0.010799999999999999, - 0.0109, - 0.011, - 0.011099999999999999, - 0.0112, - 0.0113, - 0.0114, - 0.0115, - 0.0116, - 0.0117, - 0.0118, - 0.0119, - 0.012, - 0.0121, - 0.0122, - 0.0123, - 0.0124, - 0.0125, - 0.0126, - 0.0127, - 0.012799999999999999, - 0.0129, - 0.013, - 0.013099999999999999, - 0.0132, - 0.0133, - 0.0134, - 0.0135, - 0.0136, - 0.0137, - 0.0138, - 0.013900000000000001, - 0.014, - 0.0141, - 0.0142, - 0.0143, - 0.0144, - 0.014499999999999999, - 0.0146, - 0.0147, - 0.014799999999999999, - 0.0149, - 0.015, - 0.015099999999999999, - 0.0152, - 0.0153, - 0.0154, - 0.0155, - 0.0156, - 0.0157, - 0.0158, - 0.0159, - 0.016, - 0.0161, - 0.0162, - 0.016300000000000002, - 0.0164, - 0.0165, - 0.0166, - 0.0167, - 0.016800000000000002, - 0.016900000000000002, - 0.017, - 0.0171, - 0.0172, - 0.0173, - 0.0174, - 0.0175, - 0.0176, - 0.0177, - 0.0178, - 0.0179, - 0.018, - 0.018099999999999998, - 0.018199999999999997, - 0.0183, - 0.0184, - 0.0185, - 0.0186, - 0.018699999999999998, - 0.0188, - 0.0189, - 0.019, - 0.0191, - 0.0192, - 0.0193, - 0.0194, - 0.0195, - 0.0196, - 0.0197, - 0.0198, - 0.0199, - 0.02, - 0.0201, - 0.0202, - 0.020300000000000002, - 0.0204, - 0.0205, - 0.0206, - 0.0207, - 0.020800000000000003, - 0.020900000000000002, - 0.021, - 0.0211, - 0.0212, - 0.0213, - 0.0214, - 0.0215, - 0.0216, - 0.0217, - 0.0218, - 0.0219, - 0.022, - 0.022099999999999998, - 0.022199999999999998, - 0.0223, - 0.0224, - 0.0225, - 0.0226, - 0.022699999999999998, - 0.0228, - 0.0229, - 0.023, - 0.0231, - 0.0232, - 0.0233, - 0.0234, - 0.0235, - 0.0236, - 0.0237, - 0.0238, - 0.0239, - 0.024, - 0.0241, - 0.0242, - 0.024300000000000002, - 0.0244, - 0.0245, - 0.0246, - 0.0247, - 0.024800000000000003, - 0.024900000000000002, - 0.025, - 0.0251, - 0.0252, - 0.0253, - 0.0254, - 0.025500000000000002, - 0.0256, - 0.0257, - 0.0258, - 0.0259, - 0.026, - 0.026099999999999998, - 0.026199999999999998, - 0.0263, - 0.0264, - 0.0265, - 0.0266, - 0.026699999999999998, - 0.0268, - 0.0269, - 0.027, - 0.0271, - 0.0272, - 0.0273, - 0.0274, - 0.0275, - 0.0276, - 0.0277, - 0.027800000000000002, - 0.0279, - 0.028, - 0.0281, - 0.0282, - 0.028300000000000002, - 0.0284, - 0.0285, - 0.0286, - 0.0287, - 0.028800000000000003, - 0.028900000000000002, - 0.029, - 0.0291, - 0.0292, - 0.0293, - 0.0294, - 0.0295, - 0.0296, - 0.0297, - 0.0298, - 0.0299, - 0.03, - 0.0301, - 0.030199999999999998, - 0.0303, - 0.0304, - 0.0305, - 0.0306, - 0.030699999999999998, - 0.0308, - 0.0309, - 0.031, - 0.0311, - 0.0312, - 0.0313, - 0.0314, - 0.0315, - 0.0316, - 0.0317, - 0.0318, - 0.0319, - 0.032, - 0.032100000000000004, - 0.0322, - 0.0323, - 0.0324, - 0.0325, - 0.032600000000000004, - 0.0327, - 0.0328, - 0.0329, - 0.033, - 0.033100000000000004, - 0.0332, - 0.0333, - 0.0334, - 0.0335, - 0.033600000000000005, - 0.0337, - 0.033800000000000004, - 0.0339, - 0.034, - 0.034100000000000005, - 0.0342, - 0.034300000000000004, - 0.0344, - 0.0345, - 0.034600000000000006, - 0.0347, - 0.034800000000000005, - 0.0349, - 0.035, - 0.035100000000000006, - 0.0352, - 0.0353, - 0.0354, - 0.035500000000000004, - 0.0356, - 0.035699999999999996, - 0.0358, - 0.0359, - 0.036, - 0.0361, - 0.036199999999999996, - 0.0363, - 0.036399999999999995, - 0.0365, - 0.0366, - 0.036699999999999997, - 0.0368, - 0.036899999999999995, - 0.037, - 0.0371, - 0.0372, - 0.0373, - 0.037399999999999996, - 0.0375, - 0.0376, - 0.0377, - 0.0378, - 0.037899999999999996, - 0.038, - 0.0381, - 0.0382, - 0.0383, - 0.0384, - 0.0385, - 0.0386, - 0.0387, - 0.0388, - 0.0389, - 0.039, - 0.0391, - 0.0392, - 0.0393, - 0.0394, - 0.0395, - 0.0396, - 0.0397, - 0.0398, - 0.0399, - 0.04, - 0.040100000000000004, - 0.0402, - 0.0403, - 0.0404, - 0.0405, - 0.040600000000000004, - 0.0407, - 0.0408, - 0.0409, - 0.041, - 0.041100000000000005, - 0.0412, - 0.0413, - 0.0414, - 0.0415, - 0.041600000000000005, - 0.0417, - 0.041800000000000004, - 0.0419, - 0.042, - 0.0421, - 0.0422, - 0.042300000000000004, - 0.0424, - 0.042499999999999996, - 0.0426, - 0.0427, - 0.0428, - 0.042899999999999994, - 0.043, - 0.0431, - 0.043199999999999995, - 0.0433, - 0.043399999999999994, - 0.0435, - 0.0436, - 0.043699999999999996, - 0.0438, - 0.043899999999999995, - 0.044, - 0.0441, - 0.044199999999999996, - 0.0443, - 0.044399999999999995, - 0.0445, - 0.0446, - 0.0447, - 0.0448, - 0.044899999999999995, - 0.045, - 0.0451, - 0.0452, - 0.0453, - 0.045399999999999996, - 0.0455, - 0.0456, - 0.0457, - 0.0458, - 0.045899999999999996, - 0.046, - 0.0461, - 0.0462, - 0.0463, - 0.0464, - 0.0465, - 0.0466, - 0.0467, - 0.0468, - 0.0469, - 0.047, - 0.0471, - 0.0472, - 0.0473, - 0.0474, - 0.0475, - 0.0476, - 0.0477, - 0.0478, - 0.0479, - 0.048, - 0.048100000000000004, - 0.0482, - 0.0483, - 0.0484, - 0.0485, - 0.048600000000000004, - 0.0487, - 0.0488, - 0.0489, - 0.049, - 0.049100000000000005, - 0.0492, - 0.049300000000000004, - 0.0494, - 0.0495, - 0.049600000000000005, - 0.0497, - 0.049800000000000004, - 0.0499, - 0.05, - 0.055, - 0.060000000000000005, - 0.065, - 0.07, - 0.07500000000000001, - 0.08, - 0.085, - 0.09, - 0.095, - 0.1, - 0.11, - 0.12000000000000001, - 0.13, - 0.14, - 0.15000000000000002, - 0.16, - 0.17, - 0.18, - 0.19, - 0.24374, - 0.28748, - 0.33121999999999996, - 0.37495999999999996, - 0.41869999999999996, - 0.46243999999999996, - 0.6374 - ] -} \ No newline at end of file diff --git a/pkg/aws/cloudwatch/histograms/testdata/exponential/weibull.json b/pkg/aws/cloudwatch/histograms/testdata/exponential/weibull.json deleted file mode 100644 index fa323327e1f39..0000000000000 --- a/pkg/aws/cloudwatch/histograms/testdata/exponential/weibull.json +++ /dev/null @@ -1,1024 +0,0 @@ -{ - "counts": [ - 81, - 80, - 80, - 80, - 80, - 80, - 80, - 80, - 80, - 80, - 73, - 73, - 73, - 73, - 73, - 73, - 73, - 73, - 73, - 73, - 66, - 66, - 66, - 66, - 66, - 66, - 66, - 66, - 65, - 65, - 59, - 59, - 58, - 58, - 58, - 58, - 58, - 58, - 58, - 58, - 53, - 53, - 53, - 53, - 52, - 52, - 52, - 52, - 52, - 52, - 47, - 47, - 47, - 47, - 46, - 46, - 46, - 46, - 46, - 46, - 43, - 43, - 43, - 43, - 43, - 42, - 42, - 42, - 42, - 42, - 46, - 46, - 46, - 46, - 46, - 46, - 46, - 45, - 45, - 45, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 37, - 37, - 37, - 37, - 36, - 36, - 36, - 36, - 36, - 36, - 31, - 31, - 30, - 30, - 30, - 30, - 30, - 30, - 30, - 30, - 31, - 31, - 31, - 30, - 30, - 30, - 30, - 30, - 30, - 30, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 28, - 25, - 25, - 25, - 25, - 25, - 25, - 24, - 24, - 24, - 24, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 23, - 23, - 23, - 23, - 23, - 23, - 19, - 18, - 18, - 18, - 18, - 18, - 18, - 18, - 18, - 18, - 22, - 22, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 19, - 19, - 19, - 19, - 19, - 19, - 19, - 19, - 19, - 19, - 16, - 16, - 16, - 16, - 16, - 16, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 14, - 14, - 14, - 14, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 13, - 13, - 13, - 13, - 13, - 13, - 12, - 12, - 12, - 12, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 12, - 13, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 9, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 7, - 7, - 10, - 10, - 10, - 10, - 10, - 9, - 9, - 9, - 9, - 9, - 7, - 7, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 6, - 6, - 6, - 7, - 7, - 7, - 7, - 7, - 6, - 6, - 6, - 6, - 6, - 8, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 6, - 6, - 6, - 6, - 6, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 3, - 3, - 3, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 5, - 4, - 4, - 4, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 3, - 3, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 4, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 6, - 5, - 4, - 3, - 3, - 2, - 65, - 53, - 42, - 32, - 24, - 16, - 10, - 5, - 2, - 3, - 3, - 2, - 2 - ], - "values": [ - 0.00010002863800000001, - 0.00020002545600000002, - 0.000300022274, - 0.000400019092, - 0.0005000159100000001, - 0.000600012728, - 0.0007000095460000001, - 0.0008000063640000001, - 0.0009000031820000001, - 0.001, - 0.0011, - 0.0012000000000000001, - 0.0013, - 0.0014, - 0.0015, - 0.0016, - 0.0017000000000000001, - 0.0018, - 0.0019000000000000002, - 0.002, - 0.0021, - 0.0022, - 0.0023, - 0.0024000000000000002, - 0.0025, - 0.0026, - 0.0027, - 0.0028, - 0.0029000000000000002, - 0.003, - 0.0031, - 0.0032, - 0.0033, - 0.0034000000000000002, - 0.0035, - 0.0036, - 0.0037, - 0.0038, - 0.0039000000000000003, - 0.004, - 0.0041, - 0.0042, - 0.0043, - 0.0044, - 0.0045000000000000005, - 0.0046, - 0.0047, - 0.0048000000000000004, - 0.0049, - 0.005, - 0.0051, - 0.0052, - 0.0053, - 0.0054, - 0.0055, - 0.0056, - 0.0057, - 0.0058000000000000005, - 0.0059, - 0.006, - 0.0061, - 0.0062, - 0.0063, - 0.0064, - 0.006500000000000001, - 0.0066, - 0.0067, - 0.0068000000000000005, - 0.0069, - 0.007, - 0.0071, - 0.0072, - 0.0073, - 0.0074, - 0.0075, - 0.0076, - 0.0077, - 0.0078000000000000005, - 0.0079, - 0.008, - 0.0081, - 0.0082, - 0.0083, - 0.0084, - 0.0085, - 0.0086, - 0.0087, - 0.008799999999999999, - 0.0089, - 0.009, - 0.009099999999999999, - 0.0092, - 0.0093, - 0.0094, - 0.0095, - 0.0096, - 0.0097, - 0.0098, - 0.0099, - 0.01, - 0.0101, - 0.0102, - 0.0103, - 0.0104, - 0.010499999999999999, - 0.0106, - 0.0107, - 0.010799999999999999, - 0.0109, - 0.011, - 0.011099999999999999, - 0.0112, - 0.0113, - 0.0114, - 0.0115, - 0.0116, - 0.0117, - 0.0118, - 0.0119, - 0.012, - 0.0121, - 0.0122, - 0.0123, - 0.0124, - 0.0125, - 0.0126, - 0.0127, - 0.012799999999999999, - 0.0129, - 0.013, - 0.013099999999999999, - 0.0132, - 0.0133, - 0.0134, - 0.0135, - 0.0136, - 0.0137, - 0.0138, - 0.013900000000000001, - 0.014, - 0.0141, - 0.0142, - 0.0143, - 0.0144, - 0.014499999999999999, - 0.0146, - 0.0147, - 0.014799999999999999, - 0.0149, - 0.015, - 0.015099999999999999, - 0.0152, - 0.0153, - 0.0154, - 0.0155, - 0.0156, - 0.0157, - 0.0158, - 0.0159, - 0.016, - 0.0161, - 0.0162, - 0.016300000000000002, - 0.0164, - 0.0165, - 0.0166, - 0.0167, - 0.016800000000000002, - 0.016900000000000002, - 0.017, - 0.0171, - 0.0172, - 0.0173, - 0.0174, - 0.0175, - 0.0176, - 0.0177, - 0.0178, - 0.0179, - 0.018, - 0.018099999999999998, - 0.018199999999999997, - 0.0183, - 0.0184, - 0.0185, - 0.0186, - 0.018699999999999998, - 0.0188, - 0.0189, - 0.019, - 0.0191, - 0.0192, - 0.0193, - 0.0194, - 0.0195, - 0.0196, - 0.0197, - 0.0198, - 0.0199, - 0.02, - 0.0201, - 0.0202, - 0.020300000000000002, - 0.0204, - 0.0205, - 0.0206, - 0.0207, - 0.020800000000000003, - 0.020900000000000002, - 0.021, - 0.0211, - 0.0212, - 0.0213, - 0.0214, - 0.0215, - 0.0216, - 0.0217, - 0.0218, - 0.0219, - 0.022, - 0.022099999999999998, - 0.022199999999999998, - 0.0223, - 0.0224, - 0.0225, - 0.0226, - 0.022699999999999998, - 0.0228, - 0.0229, - 0.023, - 0.0231, - 0.0232, - 0.0233, - 0.0234, - 0.0235, - 0.0236, - 0.0237, - 0.0238, - 0.0239, - 0.024, - 0.0241, - 0.0242, - 0.024300000000000002, - 0.0244, - 0.0245, - 0.0246, - 0.0247, - 0.024800000000000003, - 0.024900000000000002, - 0.025, - 0.0251, - 0.0252, - 0.0253, - 0.0254, - 0.025500000000000002, - 0.0256, - 0.0257, - 0.0258, - 0.0259, - 0.026, - 0.026099999999999998, - 0.026199999999999998, - 0.0263, - 0.0264, - 0.0265, - 0.0266, - 0.026699999999999998, - 0.0268, - 0.0269, - 0.027, - 0.0271, - 0.0272, - 0.0273, - 0.0274, - 0.0275, - 0.0276, - 0.0277, - 0.027800000000000002, - 0.0279, - 0.028, - 0.0281, - 0.0282, - 0.028300000000000002, - 0.0284, - 0.0285, - 0.0286, - 0.0287, - 0.028800000000000003, - 0.028900000000000002, - 0.029, - 0.0291, - 0.0292, - 0.0293, - 0.0294, - 0.0295, - 0.0296, - 0.0297, - 0.0298, - 0.0299, - 0.03, - 0.0301, - 0.030199999999999998, - 0.0303, - 0.0304, - 0.0305, - 0.0306, - 0.030699999999999998, - 0.0308, - 0.0309, - 0.031, - 0.0311, - 0.0312, - 0.0313, - 0.0314, - 0.0315, - 0.0316, - 0.0317, - 0.0318, - 0.0319, - 0.032, - 0.032100000000000004, - 0.0322, - 0.0323, - 0.0324, - 0.0325, - 0.032600000000000004, - 0.0327, - 0.0328, - 0.0329, - 0.033, - 0.033100000000000004, - 0.0332, - 0.0333, - 0.0334, - 0.0335, - 0.033600000000000005, - 0.0337, - 0.033800000000000004, - 0.0339, - 0.034, - 0.034100000000000005, - 0.0342, - 0.034300000000000004, - 0.0344, - 0.0345, - 0.034600000000000006, - 0.0347, - 0.034800000000000005, - 0.0349, - 0.035, - 0.035100000000000006, - 0.0352, - 0.0353, - 0.0354, - 0.035500000000000004, - 0.0356, - 0.035699999999999996, - 0.0358, - 0.0359, - 0.036, - 0.0361, - 0.036199999999999996, - 0.0363, - 0.036399999999999995, - 0.0365, - 0.0366, - 0.036699999999999997, - 0.0368, - 0.036899999999999995, - 0.037, - 0.0371, - 0.0372, - 0.0373, - 0.037399999999999996, - 0.0375, - 0.0376, - 0.0377, - 0.0378, - 0.037899999999999996, - 0.038, - 0.0381, - 0.0382, - 0.0383, - 0.0384, - 0.0385, - 0.0386, - 0.0387, - 0.0388, - 0.0389, - 0.039, - 0.0391, - 0.0392, - 0.0393, - 0.0394, - 0.0395, - 0.0396, - 0.0397, - 0.0398, - 0.0399, - 0.04, - 0.040100000000000004, - 0.0402, - 0.0403, - 0.0404, - 0.0405, - 0.040600000000000004, - 0.0407, - 0.0408, - 0.0409, - 0.041, - 0.041100000000000005, - 0.0412, - 0.0413, - 0.0414, - 0.0415, - 0.041600000000000005, - 0.0417, - 0.041800000000000004, - 0.0419, - 0.042, - 0.0421, - 0.0422, - 0.042300000000000004, - 0.0424, - 0.042499999999999996, - 0.0426, - 0.0427, - 0.0428, - 0.042899999999999994, - 0.043, - 0.0431, - 0.043199999999999995, - 0.0433, - 0.043399999999999994, - 0.0435, - 0.0436, - 0.043699999999999996, - 0.0438, - 0.043899999999999995, - 0.044, - 0.0441, - 0.044199999999999996, - 0.0443, - 0.044399999999999995, - 0.0445, - 0.0446, - 0.0447, - 0.0448, - 0.044899999999999995, - 0.045, - 0.0451, - 0.0452, - 0.0453, - 0.045399999999999996, - 0.0455, - 0.0456, - 0.0457, - 0.0458, - 0.045899999999999996, - 0.046, - 0.0461, - 0.0462, - 0.0463, - 0.0464, - 0.0465, - 0.0466, - 0.0467, - 0.0468, - 0.0469, - 0.047, - 0.0471, - 0.0472, - 0.0473, - 0.0474, - 0.0475, - 0.0476, - 0.0477, - 0.0478, - 0.0479, - 0.048, - 0.048100000000000004, - 0.0482, - 0.0483, - 0.0484, - 0.0485, - 0.048600000000000004, - 0.0487, - 0.0488, - 0.0489, - 0.049, - 0.049100000000000005, - 0.0492, - 0.049300000000000004, - 0.0494, - 0.0495, - 0.049600000000000005, - 0.055, - 0.060000000000000005, - 0.065, - 0.07, - 0.07500000000000001, - 0.08, - 0.085, - 0.09, - 0.095, - 0.11, - 0.12000000000000001, - 0.13, - 0.1332 - ] -} \ No newline at end of file From bc3018dbf0974113e1a646f17c5efd45efab190d Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Thu, 16 Oct 2025 16:11:04 -0400 Subject: [PATCH 18/23] fixup histogram mapper --- .../histograms/testdata/histogram_mappings.py | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py b/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py index b507d34458948..ea45cd8a86751 100644 --- a/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py +++ b/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py @@ -9,8 +9,8 @@ from pathlib import Path from typing import Dict, List, Tuple -def plot_original_histogram(data, ax, title: str, color: str): - """Plot original histogram using exact bucket boundaries.""" +def plot_input_histogram(data, ax, title: str, color: str): + """Plot input histogram using exact bucket boundaries.""" boundaries = data.get('Boundaries', []) counts = data['Counts'] min_val = data.get('Min') @@ -102,8 +102,8 @@ def load_json_data(filepath): data = json.load(f) return data['values'], data['counts'] -def load_original_histogram(filepath): - """Load original histogram format.""" +def load_input_histogram(filepath): + """Load input histogram format.""" with open(filepath, 'r') as f: data = json.load(f) return data @@ -111,8 +111,8 @@ def load_original_histogram(filepath): def plot_all_folders_comparison(json_filename): """Plot the same JSON file from all folders for comparison.""" base_path = Path('.') - folders = ['original', 'cwagent', 'even', 'middlepoint', 'exponential', 'exponentialcw'] - colors = ['black', 'green', 'orange', 'red', 'purple', 'blue'] + folders = ['input', 'exponential'] + colors = ['black', 'green'] fig, ax = plt.subplots(len(folders), 1, figsize=(12, 20)) @@ -122,9 +122,9 @@ def plot_all_folders_comparison(json_filename): filepath = base_path / folder / (json_filename+".json") if filepath.exists(): try: - if folder == 'original': - data = load_original_histogram(filepath) - plot_original_histogram(data, ax[i], f'{folder.capitalize()} Mapping', color) + if folder == 'input': + data = load_input_histogram(filepath) + plot_input_histogram(data, ax[i], f'{folder.capitalize()} Mapping', color) else: values, counts = load_json_data(filepath) if not values: # Skip if no values @@ -144,23 +144,25 @@ def plot_all_folders_comparison(json_filename): parser.add_argument('dataset', nargs='?', help='Optional dataset name to process') args = parser.parse_args() - original_path = Path('./original') - if original_path.exists(): + os.makedirs('comparisons', exist_ok=True) + + input_path = Path('./input') + if input_path.exists(): if args.dataset: # Process specific dataset if provided - dataset_file = original_path / f"{args.dataset}.json" + dataset_file = input_path / f"{args.dataset}.json" if dataset_file.exists(): print(f"Processing {args.dataset}...") plot_all_folders_comparison(args.dataset) else: - print(f"Dataset '{args.dataset}' not found in original folder.") + print(f"Dataset '{args.dataset}' not found in input folder.") else: # Process all datasets if no specific dataset provided - json_files = [f.stem for f in original_path.iterdir() if f.suffix == '.json'] + json_files = [f.stem for f in input_path.iterdir() if f.suffix == '.json'] for json_file in json_files: print(f"Processing {json_file}...") plot_all_folders_comparison(json_file) else: - print("Original folder not found.") + print("Input folder not found.") From 3e5e7d2ae3446f330ec77c01c8c7c3ae5ebcff8d Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Thu, 16 Oct 2025 16:16:19 -0400 Subject: [PATCH 19/23] Ignore generated file --- pkg/aws/cloudwatch/histograms/conversion_test.go | 6 ++++-- pkg/aws/cloudwatch/histograms/testdata/.gitignore | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 pkg/aws/cloudwatch/histograms/testdata/.gitignore diff --git a/pkg/aws/cloudwatch/histograms/conversion_test.go b/pkg/aws/cloudwatch/histograms/conversion_test.go index 8c58acd750ca2..a8d980c78c6fd 100644 --- a/pkg/aws/cloudwatch/histograms/conversion_test.go +++ b/pkg/aws/cloudwatch/histograms/conversion_test.go @@ -26,9 +26,11 @@ var filenameReplacer = strings.NewReplacer( ) func TestWriteInputHistograms(t *testing.T) { + t.Skip("only used to create test data for visualization") for _, tc := range TestCases() { jsonData, err := json.MarshalIndent(tc.Input, "", " ") require.NoError(t, err) + os.Mkdir("testdata/input", os.ModePerm) require.NoError(t, os.WriteFile("testdata/input/"+filenameReplacer.Replace(tc.Name)+".json", jsonData, 0644)) } } @@ -43,8 +45,8 @@ func TestConvertOTelToCloudWatch(t *testing.T) { // uncomment next lines to write datapoint to JSON file for visual inspection // use histogram_mappings.py to create graphs - - // assert.NoError(t, writeValuesAndCountsToJson(dist, "testdata/exponential/"+filenameReplacer.Replace(tc.Name+".json"))) + //os.Mkdir("testdata/exponential", os.ModePerm) + //assert.NoError(t, writeValuesAndCountsToJson(dist, "testdata/exponential/"+filenameReplacer.Replace(tc.Name+".json"))) }) } diff --git a/pkg/aws/cloudwatch/histograms/testdata/.gitignore b/pkg/aws/cloudwatch/histograms/testdata/.gitignore new file mode 100644 index 0000000000000..0f0d6bdaf849c --- /dev/null +++ b/pkg/aws/cloudwatch/histograms/testdata/.gitignore @@ -0,0 +1,3 @@ +comparisons/ +exponential/ +input/ \ No newline at end of file From a300ff5f0d5416affc6cf3044817d1cf24163e0e Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Thu, 16 Oct 2025 16:17:05 -0400 Subject: [PATCH 20/23] Remove unneeded cloudwatch pusher tool --- pkg/aws/cloudwatch/tool/cloudwatch_pusher.go | 183 ------------------- pkg/aws/cloudwatch/tool/go.mod | 23 --- pkg/aws/cloudwatch/tool/go.sum | 28 --- 3 files changed, 234 deletions(-) delete mode 100644 pkg/aws/cloudwatch/tool/cloudwatch_pusher.go delete mode 100644 pkg/aws/cloudwatch/tool/go.mod delete mode 100644 pkg/aws/cloudwatch/tool/go.sum diff --git a/pkg/aws/cloudwatch/tool/cloudwatch_pusher.go b/pkg/aws/cloudwatch/tool/cloudwatch_pusher.go deleted file mode 100644 index 198678027e937..0000000000000 --- a/pkg/aws/cloudwatch/tool/cloudwatch_pusher.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package aws - -import ( - "context" - "encoding/json" - "fmt" - "io/fs" - "os" - "path/filepath" - "sort" - "strings" - "text/tabwriter" - "time" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" -) - -type DistributionData struct { - Values []float64 `json:"values"` - Counts []float64 `json:"counts"` -} - -type MetricInfo struct { - TestCase string - MappingType string - MetricName string -} - -func main() { - ctx := context.Background() - cfg, err := config.LoadDefaultConfig(ctx) - if err != nil { - panic(err) - } - - cw := cloudwatch.NewFromConfig(cfg) - testDataDir := "testdata" - mappingTypes := []string{"cwagent", "exponential", "exponentialcw", "middlepoint", "even"} - - var metrics []MetricInfo - - // Push data to CloudWatch - for _, mappingType := range mappingTypes { - dirPath := filepath.Join(testDataDir, mappingType) - err := filepath.WalkDir(dirPath, func(path string, d fs.DirEntry, err error) error { - - if err != nil || !strings.HasSuffix(path, ".json") { - return err - } - - fileName := strings.TrimSuffix(filepath.Base(path), ".json") - metricName := fmt.Sprintf("HistogramDistribution_%s", mappingType) - metrics = append(metrics, MetricInfo{fileName, mappingType, metricName}) - //return nil - if err := pushDistributionToCloudWatch(ctx, cw, path, mappingType); err != nil { - fmt.Printf("Error pushing %s: %v\n", path, err) - } - return nil - }) - - if err != nil { - fmt.Printf("Error processing %s: %v\n", mappingType, err) - } - } - - // Wait for metrics to be available - fmt.Println("Waiting for metrics to be available...") - time.Sleep(30 * time.Second) - - // Query percentiles and display table - queryPercentilesAndDisplayTable(ctx, cw, metrics) -} - -func pushDistributionToCloudWatch(ctx context.Context, cw *cloudwatch.Client, filePath, mappingType string) error { - data, err := os.ReadFile(filePath) - if err != nil { - return err - } - - var dist DistributionData - if err := json.Unmarshal(data, &dist); err != nil { - return err - } - - fileName := strings.TrimSuffix(filepath.Base(filePath), ".json") - metricName := fmt.Sprintf("HistogramDistribution_%s", mappingType) - - // Convert to CloudWatch distribution format - values := make([]float64, len(dist.Values)) - counts := make([]float64, len(dist.Counts)) - copy(values, dist.Values) - copy(counts, dist.Counts) - - _, err = cw.PutMetricData(ctx, &cloudwatch.PutMetricDataInput{ - Namespace: aws.String("HistogramTesting"), - MetricData: []types.MetricDatum{ - { - MetricName: aws.String(metricName), - Dimensions: []types.Dimension{ - { - Name: aws.String("TestCase"), - Value: aws.String(fileName), - }, - { - Name: aws.String("MappingType"), - Value: aws.String(mappingType), - }, - }, - Timestamp: aws.Time(time.Now()), - Values: values, - Counts: counts, - }, - }, - }) - - if err != nil { - return fmt.Errorf("failed to push %s: %w", filePath, err) - } - - fmt.Printf("Pushed %s (%s)\n", fileName, mappingType) - return nil -} - -func queryPercentilesAndDisplayTable(ctx context.Context, cw *cloudwatch.Client, metrics []MetricInfo) { - percentiles := []string{"10", "25", "50", "75", "90", "99", "99.9"} - endTime := time.Now() - startTime := endTime.Add(-1 * time.Minute) - - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - fmt.Fprintln(w, "TestCase\tMappingType\tP10\tP25\tP50\tP75\tP90\tP99\tP99.9") - - // Group by test case for better organization - testCases := make(map[string][]MetricInfo) - for _, metric := range metrics { - testCases[metric.TestCase] = append(testCases[metric.TestCase], metric) - } - - // Sort test cases for consistent output - var sortedTestCases []string - for testCase := range testCases { - sortedTestCases = append(sortedTestCases, testCase) - } - sort.Strings(sortedTestCases) - - for _, testCase := range sortedTestCases { - for _, metric := range testCases[testCase] { - values := make([]string, len(percentiles)) - for i, p := range percentiles { - stat := fmt.Sprintf("p%s", p) - resp, err := cw.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{ - Namespace: aws.String("HistogramTesting"), - MetricName: aws.String(metric.MetricName), - Dimensions: []types.Dimension{ - {Name: aws.String("TestCase"), Value: aws.String(metric.TestCase)}, - {Name: aws.String("MappingType"), Value: aws.String(metric.MappingType)}, - }, - StartTime: aws.Time(startTime), - EndTime: aws.Time(endTime), - Period: aws.Int32(300), - ExtendedStatistics: []string{stat}, - }) - if err != nil { - fmt.Printf("Error querying %s: %v\n", metric.MetricName, err) - } - if err != nil || len(resp.Datapoints) == 0 { - values[i] = "N/A" - } else { - values[i] = fmt.Sprintf("%.5f", resp.Datapoints[0].ExtendedStatistics[stat]) - } - } - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", - metric.TestCase, metric.MappingType, - values[0], values[1], values[2], values[3], values[4], values[5], values[6]) - } - } - w.Flush() -} diff --git a/pkg/aws/cloudwatch/tool/go.mod b/pkg/aws/cloudwatch/tool/go.mod deleted file mode 100644 index 2e6d33f258601..0000000000000 --- a/pkg/aws/cloudwatch/tool/go.mod +++ /dev/null @@ -1,23 +0,0 @@ -module cloudwtach_pusher - -go 1.25.0 - -require ( - github.com/aws/aws-sdk-go-v2 v1.39.2 - github.com/aws/aws-sdk-go-v2/config v1.31.12 - github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.51.1 -) - -require ( - github.com/aws/aws-sdk-go-v2/credentials v1.18.16 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 // indirect - github.com/aws/smithy-go v1.23.0 // indirect -) diff --git a/pkg/aws/cloudwatch/tool/go.sum b/pkg/aws/cloudwatch/tool/go.sum deleted file mode 100644 index 4c421a84a4ac1..0000000000000 --- a/pkg/aws/cloudwatch/tool/go.sum +++ /dev/null @@ -1,28 +0,0 @@ -github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= -github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= -github.com/aws/aws-sdk-go-v2/config v1.31.12 h1:pYM1Qgy0dKZLHX2cXslNacbcEFMkDMl+Bcj5ROuS6p8= -github.com/aws/aws-sdk-go-v2/config v1.31.12/go.mod h1:/MM0dyD7KSDPR+39p9ZNVKaHDLb9qnfDurvVS2KAhN8= -github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= -github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 h1:Mv4Bc0mWmv6oDuSWTKnk+wgeqPL5DRFu5bQL9BGPQ8Y= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9/go.mod h1:IKlKfRppK2a1y0gy1yH6zD+yX5uplJ6UuPlgd48dJiQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.51.1 h1:GqVafesryYki8Lw/yRzLcoSeaT06qSAIbLoZLqeY0ks= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.51.1/go.mod h1:Kg/y+WTU5U8KtZ8vYYz0CyiR8UCBbZkpsT7TeqIkQ2M= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 h1:5r34CgVOD4WZudeEKZ9/iKpiT6cM1JyEROpXjOcdWv8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9/go.mod h1:dB12CEbNWPbzO2uC6QSWHteqOg4JfBVJOojbAoAUb5I= -github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 h1:A1oRkiSQOWstGh61y4Wc/yQ04sqrQZr1Si/oAXj20/s= -github.com/aws/aws-sdk-go-v2/service/sso v1.29.6/go.mod h1:5PfYspyCU5Vw1wNPsxi15LZovOnULudOQuVxphSflQA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 h1:5fm5RTONng73/QA73LhCNR7UT9RpFH3hR6HWL6bIgVY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1/go.mod h1:xBEjWD13h+6nq+z4AkqSfSvqRKFgDIQeaMguAJndOWo= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47hZb5HUQ0tn6Q9kA= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= -github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= -github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= From 2f98f9edc1a99a151d80766c02a126f3d804597b Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Thu, 16 Oct 2025 16:18:02 -0400 Subject: [PATCH 21/23] Moved out of share folder --- share/testdata/histograms/generator.go | 266 ------- share/testdata/histograms/generator_test.go | 18 - share/testdata/histograms/go.mod | 11 - share/testdata/histograms/go.sum | 10 - share/testdata/histograms/histograms.go | 728 ------------------- share/testdata/histograms/histograms_test.go | 347 --------- 6 files changed, 1380 deletions(-) delete mode 100644 share/testdata/histograms/generator.go delete mode 100644 share/testdata/histograms/generator_test.go delete mode 100644 share/testdata/histograms/go.mod delete mode 100644 share/testdata/histograms/go.sum delete mode 100644 share/testdata/histograms/histograms.go delete mode 100644 share/testdata/histograms/histograms_test.go diff --git a/share/testdata/histograms/generator.go b/share/testdata/histograms/generator.go deleted file mode 100644 index 1cc3e374cdd02..0000000000000 --- a/share/testdata/histograms/generator.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT - -package histograms - -import ( - "math" - "math/rand/v2" - "sort" -) - -type DistributionConfig struct { - Name string - SampleSize int - Params map[string]float64 -} - -type GeneratedDataset struct { - Name string - Data []float64 - ActualPercentiles map[float64]float64 - Histograms []HistogramTestCase -} - -func GenerateTestData() []GeneratedDataset { - return GenerateTestDataWithSeed(42) -} - -func GenerateTestDataWithSeed(seed uint64) []GeneratedDataset { - - configs := []DistributionConfig{ - {Name: "LogNormal", SampleSize: 2000, Params: map[string]float64{"mu": 2.0, "sigma": 0.5}}, - {Name: "Weibull", SampleSize: 2000, Params: map[string]float64{"shape": 2.0, "scale": 100.0}}, - {Name: "Normal", SampleSize: 2000, Params: map[string]float64{"mean": 50.0, "stddev": 15.0}}, - {Name: "Gamma", SampleSize: 2000, Params: map[string]float64{"shape": 2.0, "rate": 0.1}}, - } - - var datasets []GeneratedDataset - for _, config := range configs { - // each dataset gets a copy of the same rand so that they don't interfere with each other - rng := rand.New(rand.NewPCG(seed, seed)) - data := generateSamples(config, rng) - percentiles := calculatePercentiles(data, []float64{0.01, 0.25, 0.5, 0.75, 0.99}) - histograms := createHistograms(config.Name, data, percentiles) - - datasets = append(datasets, GeneratedDataset{ - Name: config.Name, - Data: data, - ActualPercentiles: percentiles, - Histograms: histograms, - }) - } - - return datasets -} - -func generateSamples(config DistributionConfig, rng *rand.Rand) []float64 { - data := make([]float64, config.SampleSize) - - switch config.Name { - case "LogNormal": - mu, sigma := config.Params["mu"], config.Params["sigma"] - for i := 0; i < config.SampleSize; i++ { - data[i] = math.Exp(rng.NormFloat64()*sigma + mu) - } - case "Weibull": - shape, scale := config.Params["shape"], config.Params["scale"] - for i := 0; i < config.SampleSize; i++ { - u := rng.Float64() - data[i] = scale * math.Pow(-math.Log(1-u), 1/shape) - } - case "Normal": - mean, stddev := config.Params["mean"], config.Params["stddev"] - for i := 0; i < config.SampleSize; i++ { - data[i] = rng.NormFloat64()*stddev + mean - } - case "Gamma": - shape, rate := config.Params["shape"], config.Params["rate"] - for i := 0; i < config.SampleSize; i++ { - data[i] = gammaRandom(shape, rng) / rate - } - } - - return data -} - -func gammaRandom(shape float64, rng *rand.Rand) float64 { - if shape < 1 { - return gammaRandom(shape+1, rng) * math.Pow(rng.Float64(), 1/shape) - } - - d := shape - 1.0/3.0 - c := 1.0 / math.Sqrt(9.0*d) - - for { - x := rng.NormFloat64() - v := 1.0 + c*x - if v <= 0 { - continue - } - v = v * v * v - u := rng.Float64() - if u < 1.0-0.0331*(x*x)*(x*x) { - return d * v - } - if math.Log(u) < 0.5*x*x+d*(1.0-v+math.Log(v)) { - return d * v - } - } -} - -func calculatePercentiles(data []float64, percentiles []float64) map[float64]float64 { - sorted := make([]float64, len(data)) - copy(sorted, data) - sort.Float64s(sorted) - - result := make(map[float64]float64) - for _, p := range percentiles { - idx := int(p * float64(len(sorted)-1)) - result[p] = sorted[idx] - } - return result -} - -func createHistograms(name string, data []float64, percentiles map[float64]float64) []HistogramTestCase { - min, max := minMax(data) - sum := sum(data) - - // Create different histogram configurations - configs := []struct { - suffix string - boundaries []float64 - }{ - {"Linear10", createLinearBoundaries(min, max, 10)}, - {"Linear20", createLinearBoundaries(min, max, 20)}, - {"Exponential", createExponentialBoundaries(min, max, 15)}, - {"Percentile", createPercentileBoundaries(data, []float64{0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.99})}, - } - - var histograms []HistogramTestCase - for _, config := range configs { - counts := calculateCounts(data, config.boundaries) - - histograms = append(histograms, HistogramTestCase{ - Name: name + "_" + config.suffix, - Input: HistogramInput{ - Count: uint64(len(data)), - Sum: sum, - Min: &min, - Max: &max, - Boundaries: config.boundaries, - Counts: counts, - Attributes: map[string]string{"distribution": name, "config": config.suffix}, - }, - Expected: ExpectedMetrics{ - Count: uint64(len(data)), - Sum: sum, - Average: sum / float64(len(data)), - Min: &min, - Max: &max, - PercentileRanges: createPercentileRanges(percentiles, config.boundaries, min, max), - }, - }) - } - - return histograms -} - -func createLinearBoundaries(min, max float64, buckets int) []float64 { - boundaries := make([]float64, buckets-1) - step := (max - min) / float64(buckets) - for i := 0; i < buckets-1; i++ { - boundaries[i] = min + float64(i+1)*step - } - return boundaries -} - -func createExponentialBoundaries(min, max float64, buckets int) []float64 { - if min <= 0 { - min = 0.1 - } - boundaries := make([]float64, buckets-1) - ratio := math.Pow(max/min, 1.0/float64(buckets)) - for i := 0; i < buckets-1; i++ { - boundaries[i] = min * math.Pow(ratio, float64(i+1)) - } - return boundaries -} - -func createPercentileBoundaries(data []float64, percentiles []float64) []float64 { - sorted := make([]float64, len(data)) - copy(sorted, data) - sort.Float64s(sorted) - - boundaries := make([]float64, len(percentiles)) - for i, p := range percentiles { - idx := int(p * float64(len(sorted)-1)) - boundaries[i] = sorted[idx] - } - return boundaries -} - -func calculateCounts(data []float64, boundaries []float64) []uint64 { - counts := make([]uint64, len(boundaries)+1) - - for _, value := range data { - bucket := 0 - for i, boundary := range boundaries { - if value <= boundary { - bucket = i - break - } - bucket = i + 1 - } - counts[bucket]++ - } - - return counts -} - -func createPercentileRanges(percentiles map[float64]float64, boundaries []float64, min, max float64) map[float64]PercentileRange { - ranges := make(map[float64]PercentileRange) - - for p, value := range percentiles { - low, high := min, max - - // Find bucket containing this percentile value - for i, boundary := range boundaries { - if value <= boundary { - if i > 0 { - low = boundaries[i-1] - } - high = boundary - break - } - if i == len(boundaries)-1 { - low = boundary - } - } - - ranges[p] = PercentileRange{Low: low, High: high} - } - - return ranges -} - -func minMax(data []float64) (float64, float64) { - min, max := data[0], data[0] - for _, v := range data[1:] { - if v < min { - min = v - } - if v > max { - max = v - } - } - return min, max -} - -func sum(data []float64) float64 { - total := 0.0 - for _, v := range data { - total += v - } - return total -} diff --git a/share/testdata/histograms/generator_test.go b/share/testdata/histograms/generator_test.go deleted file mode 100644 index c6ca006fbc08f..0000000000000 --- a/share/testdata/histograms/generator_test.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT - -package histograms - -import ( - "testing" - - "github.com/amazon-contributing/opentelemetry-collector-contrib/share/testdata/histograms" -) - -func TestGenerate(t *testing.T) { - datasets := histograms.GenerateTestDataWithSeed(12345) - - for _, ds := range datasets { - - } -} diff --git a/share/testdata/histograms/go.mod b/share/testdata/histograms/go.mod deleted file mode 100644 index 740171b6172fe..0000000000000 --- a/share/testdata/histograms/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module github.com/amazon-contributing/opentelemetry-collector-contrib/share/testdata/histograms - -go 1.25.0 - -require github.com/stretchr/testify v1.11.1 - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/share/testdata/histograms/go.sum b/share/testdata/histograms/go.sum deleted file mode 100644 index c4c1710c475c1..0000000000000 --- a/share/testdata/histograms/go.sum +++ /dev/null @@ -1,10 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/share/testdata/histograms/histograms.go b/share/testdata/histograms/histograms.go deleted file mode 100644 index 46d45c766f73e..0000000000000 --- a/share/testdata/histograms/histograms.go +++ /dev/null @@ -1,728 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT - -package histograms - -import ( - "math" -) - -type HistogramTestCase struct { - Name string - Input HistogramInput - Expected ExpectedMetrics -} - -type HistogramInput struct { - Count uint64 - Sum float64 - Min *float64 - Max *float64 - Boundaries []float64 - Counts []uint64 - Attributes map[string]string -} - -type PercentileRange struct { - Low float64 - High float64 -} -type ExpectedMetrics struct { - Count uint64 - Sum float64 - Average float64 - Min *float64 - Max *float64 - PercentileRanges map[float64]PercentileRange -} - -func TestCases() []HistogramTestCase { - - // Create large bucket arrays with 11 items per bucket - boundaries125 := make([]float64, 125) - counts125 := make([]uint64, 126) - for i := 0; i < 125; i++ { - boundaries125[i] = float64(i+1) * 10 - counts125[i] = 11 - } - counts125[125] = 11 - - boundaries175 := make([]float64, 175) - counts175 := make([]uint64, 176) - for i := 0; i < 175; i++ { - boundaries175[i] = float64(i+1) * 10 - counts175[i] = 11 - } - counts175[175] = 11 - - boundaries225 := make([]float64, 225) - counts225 := make([]uint64, 226) - for i := 0; i < 225; i++ { - boundaries225[i] = float64(i+1) * 10 - counts225[i] = 11 - } - counts225[225] = 11 - - boundaries325 := make([]float64, 325) - counts325 := make([]uint64, 326) - for i := 0; i < 325; i++ { - boundaries325[i] = float64(i+1) * 10 - counts325[i] = 11 - } - counts325[325] = 11 - - return []HistogramTestCase{ - { - Name: "Basic Histogram", - Input: HistogramInput{ - Count: 101, - Sum: 6000, - Min: ptr(10.0), - Max: ptr(200.0), - Boundaries: []float64{25, 50, 75, 100, 150}, - Counts: []uint64{21, 31, 25, 15, 7, 2}, - Attributes: map[string]string{"service.name": "payment-service"}, - }, - Expected: ExpectedMetrics{ - Count: 101, - Sum: 6000, - Average: 59.41, - Min: ptr(10.0), - Max: ptr(200.0), - PercentileRanges: map[float64]PercentileRange{ - 0.01: {Low: 10.0, High: 25.0}, - 0.1: {Low: 10.0, High: 25.0}, - 0.25: {Low: 25.0, High: 50.0}, - 0.5: {Low: 25.0, High: 50.0}, - 0.75: {Low: 50.0, High: 75.0}, - 0.9: {Low: 75.0, High: 100.0}, - 0.99: {Low: 150.0, High: 200.0}, - }, - }, - }, - { - Name: "Single Bucket", - Input: HistogramInput{ - Count: 51, - Sum: 1000, - Min: ptr(5.0), - Max: ptr(75.0), - Boundaries: []float64{}, - Counts: []uint64{51}, - Attributes: map[string]string{"service.name": "auth-service"}, - }, - Expected: ExpectedMetrics{ - Count: 51, - Sum: 1000, - Average: 19.61, - Min: ptr(5.0), - Max: ptr(75.0), - PercentileRanges: map[float64]PercentileRange{ - 0.01: {Low: 5.0, High: 75.0}, - 0.1: {Low: 5.0, High: 75.0}, - 0.25: {Low: 5.0, High: 75.0}, - 0.5: {Low: 5.0, High: 75.0}, - 0.75: {Low: 5.0, High: 75.0}, - 0.9: {Low: 5.0, High: 75.0}, - 0.99: {Low: 5.0, High: 75.0}, - }, - }, - }, - { - Name: "Two Buckets", - Input: HistogramInput{ - Count: 31, - Sum: 150, - Min: ptr(1.0), - Max: ptr(10.0), - Boundaries: []float64{5}, - Counts: []uint64{21, 10}, - Attributes: map[string]string{"service.name": "database"}, - }, - Expected: ExpectedMetrics{ - Count: 31, - Sum: 150, - Average: 4.84, - Min: ptr(1.0), - Max: ptr(10.0), - PercentileRanges: map[float64]PercentileRange{ - 0.01: {Low: 1.0, High: 5.0}, - 0.1: {Low: 1.0, High: 5.0}, - 0.25: {Low: 1.0, High: 5.0}, - 0.5: {Low: 1.0, High: 5.0}, - 0.75: {Low: 5.0, High: 10.0}, - 0.9: {Low: 5.0, High: 10.0}, - 0.99: {Low: 5.0, High: 10.0}, - }, - }, - }, - { - Name: "Zero Counts and Sparse Data", - Input: HistogramInput{ - Count: 101, - Sum: 25000, - Min: ptr(0.0), - Max: ptr(1500.0), - Boundaries: []float64{10, 50, 100, 500, 1000}, - Counts: []uint64{51, 0, 0, 39, 0, 11}, - Attributes: map[string]string{"service.name": "cache-service"}, - }, - Expected: ExpectedMetrics{ - Count: 101, - Sum: 25000, - Average: 247.52, - Min: ptr(0.0), - Max: ptr(1500.0), - PercentileRanges: map[float64]PercentileRange{ - 0.01: {Low: 0.0, High: 10.0}, - 0.1: {Low: 0.0, High: 10.0}, - 0.25: {Low: 0.0, High: 10.0}, - 0.5: {Low: 0.0, High: 10.0}, - 0.75: {Low: 100.0, High: 500.0}, - 0.9: {Low: 1000.0, High: 1500.0}, - 0.99: {Low: 1000.0, High: 1500.0}, - }, - }, - }, - { - Name: "Large Numbers", - Input: HistogramInput{ - Count: 1001, - Sum: 100000000000, - Min: ptr(100000.0), - Max: ptr(1000000000.0), - Boundaries: []float64{1000000, 10000000, 50000000, 100000000, 500000000}, - Counts: []uint64{201, 301, 249, 150, 50, 50}, - Attributes: map[string]string{"service.name": "batch-processor"}, - }, - Expected: ExpectedMetrics{ - Count: 1001, - Sum: 100000000000, - Average: 99900099.90, - Min: ptr(100000.0), - Max: ptr(1000000000.0), - PercentileRanges: map[float64]PercentileRange{ - 0.01: {Low: 100000.0, High: 1000000.0}, - 0.1: {Low: 100000.0, High: 1000000.0}, - 0.25: {Low: 1000000.0, High: 10000000.0}, - 0.5: {Low: 1000000.0, High: 10000000.0}, - 0.75: {Low: 10000000.0, High: 50000000.0}, - 0.9: {Low: 50000000.0, High: 100000000.0}, - 0.99: {Low: 500000000.0, High: 1000000000.0}, - }, - }, - }, - { - Name: "Many Buckets", - Input: HistogramInput{ - Count: 1124, - Sum: 350000, - Min: ptr(0.5), - Max: ptr(1100.0), - Boundaries: []float64{1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}, - Counts: []uint64{51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 53}, - Attributes: map[string]string{"service.name": "detailed-metrics"}, - }, - Expected: ExpectedMetrics{ - Count: 1124, - Sum: 350000, - Average: 315.03, - Min: ptr(0.5), - Max: ptr(1100.0), - PercentileRanges: map[float64]PercentileRange{ - 0.01: {Low: 0.5, High: 1.0}, - 0.1: {Low: 5.0, High: 10.0}, - 0.25: {Low: 30.0, High: 40.0}, - 0.5: {Low: 90.0, High: 100.0}, - 0.75: {Low: 500.0, High: 600.0}, - 0.9: {Low: 800.0, High: 900.0}, - 0.99: {Low: 1000.0, High: 1100.0}, - }, - }, - }, - { - Name: "Very Small Numbers", - Input: HistogramInput{ - Count: 101, - Sum: 0.00015, - Min: ptr(0.00000001), - Max: ptr(0.000006), - Boundaries: []float64{0.0000001, 0.000001, 0.000002, 0.000003, 0.000004, 0.000005}, - Counts: []uint64{11, 21, 29, 20, 15, 4, 1}, - Attributes: map[string]string{"service.name": "micro-timing"}, - }, - Expected: ExpectedMetrics{ - Count: 101, - Sum: 0.00015, - Average: 0.00000149, - Min: ptr(0.00000001), - Max: ptr(0.000006), - PercentileRanges: map[float64]PercentileRange{ - 0.01: {Low: 0.00000001, High: 0.0000001}, - 0.1: {Low: 0.00000001, High: 0.0000001}, - 0.25: {Low: 0.0000001, High: 0.000001}, - 0.5: {Low: 0.000001, High: 0.000002}, - 0.75: {Low: 0.000002, High: 0.000003}, - 0.9: {Low: 0.000003, High: 0.000004}, - 0.99: {Low: 0.000004, High: 0.000005}, - }, - }, - }, - { - Name: "Only Negative Boundaries", - Input: HistogramInput{ - Count: 101, - Sum: -10000, - Min: ptr(-200.0), - Max: ptr(-10.0), - Boundaries: []float64{-150, -100, -75, -50, -25}, - Counts: []uint64{21, 31, 25, 15, 7, 2}, - Attributes: map[string]string{"service.name": "negative-service"}, - }, - Expected: ExpectedMetrics{ - Count: 101, - Sum: -10000, - Average: -59.41, - Min: ptr(-200.0), - Max: ptr(-10.0), - // Can't get percentiles for negatives - PercentileRanges: map[float64]PercentileRange{}, - }, - }, - { - Name: "Negative and Positive Boundaries", - Input: HistogramInput{ - Count: 106, - Sum: 0, - Min: ptr(-50.0), - Max: ptr(50.0), - Boundaries: []float64{-30, -10, 10, 30}, - Counts: []uint64{25, 26, 5, 25, 25}, - Attributes: map[string]string{"service.name": "temperature-service"}, - }, - Expected: ExpectedMetrics{ - Count: 106, - Sum: 0, - Average: 0.0, - Min: ptr(-50.0), - Max: ptr(50.0), - // Can't get percentiles for negatives - PercentileRanges: map[float64]PercentileRange{}, - }, - }, - - { - Name: "Positive boundaries but implied Negative Values", - Input: HistogramInput{ - Count: 101, - Sum: 200, - Min: ptr(-100.0), - Max: ptr(60.0), - Boundaries: []float64{0, 10, 20, 30, 40, 50}, - Counts: []uint64{61, 10, 10, 10, 5, 4, 1}, - Attributes: map[string]string{"service.name": "temperature-service"}, - }, - Expected: ExpectedMetrics{ - Count: 101, - Sum: 200, - Average: -29.70, - Min: ptr(-100.0), - Max: ptr(60.0), - // Can't get percentiles for negatives - PercentileRanges: map[float64]PercentileRange{}, - }, - }, - { - Name: "First bucket boundary equals minimum", - Input: HistogramInput{ - Count: 100, - Sum: 8000, - Min: ptr(10.0), - Max: ptr(160.0), - Boundaries: []float64{10, 75, 100, 150}, - Counts: []uint64{20, 30, 25, 15, 10}, - Attributes: map[string]string{"service.name": "invalid-max-bucket"}, - }, - Expected: ExpectedMetrics{ - Count: 100, - Sum: 8000, - Average: 1000, - Min: ptr(10.0), - Max: ptr(160.0), - PercentileRanges: map[float64]PercentileRange{ - 0.1: {Low: 10.0, High: 10.0}, - 0.25: {Low: 10.0, High: 75.0}, - 0.5: {Low: 75.0, High: 100.0}, - 0.75: {Low: 100.0, High: 150.0}, - 0.9: {Low: 150.0, High: 160.0}, - }, - }, - }, - { - Name: "No Min or Max", - Input: HistogramInput{ - Count: 75, - Sum: 3500, - Min: nil, - Max: nil, - Boundaries: []float64{10, 50, 100, 200}, - Counts: []uint64{15, 21, 24, 10, 5}, - Attributes: map[string]string{"service.name": "web-service"}, - }, - Expected: ExpectedMetrics{ - Count: 75, - Sum: 3500, - Average: 46.67, - Min: nil, - Max: nil, - PercentileRanges: map[float64]PercentileRange{ - 0.1: {Low: math.Inf(-1), High: 10.0}, - 0.25: {Low: 10.0, High: 50.0}, - 0.5: {Low: 50.0, High: 100.0}, - 0.75: {Low: 50.0, High: 100.0}, - 0.9: {Low: 100.0, High: 200.0}, - }, - }, - }, - { - Name: "Only Max Defined", - Input: HistogramInput{ - Count: 101, - Sum: 17500, - Min: nil, - Max: ptr(750.0), - Boundaries: []float64{100, 200, 300, 400, 500}, - Counts: []uint64{21, 31, 24, 15, 5, 5}, - Attributes: map[string]string{"service.name": "api-gateway"}, - }, - Expected: ExpectedMetrics{ - Count: 101, - Sum: 17500, - Average: 173.27, - Min: nil, - Max: ptr(750.0), - PercentileRanges: map[float64]PercentileRange{ - 0.1: {Low: math.Inf(-1), High: 100.0}, - 0.25: {Low: 100.0, High: 200.0}, - 0.5: {Low: 100.0, High: 200.0}, - 0.75: {Low: 200.0, High: 300.0}, - 0.9: {Low: 300.0, High: 400.0}, - }, - }, - }, - { - Name: "Only Min Defined", - Input: HistogramInput{ - Count: 51, - Sum: 4000, - Min: ptr(25.0), - Max: nil, - Boundaries: []float64{50, 100, 150}, - Counts: []uint64{11, 21, 14, 5}, - Attributes: map[string]string{"service.name": "queue-service"}, - }, - Expected: ExpectedMetrics{ - Count: 51, - Sum: 4000, - Average: 78.43, - Min: ptr(25.0), - Max: nil, - PercentileRanges: map[float64]PercentileRange{ - 0.1: {Low: 25.0, High: 50.0}, - 0.25: {Low: 50.0, High: 100.0}, - 0.5: {Low: 50.0, High: 100.0}, - 0.75: {Low: 100.0, High: 150.0}, - 0.9: {Low: 100.0, High: 150.0}, - }, - }, - }, - { - Name: "No Min/Max with Single Value", - Input: HistogramInput{ - Count: 1, - Sum: 100, - Min: nil, - Max: nil, - Boundaries: []float64{50, 150}, - Counts: []uint64{0, 1, 0}, - Attributes: map[string]string{"service.name": "singleton-service"}, - }, - Expected: ExpectedMetrics{ - Count: 1, - Sum: 100, - Average: 100.0, - Min: nil, - Max: nil, - PercentileRanges: map[float64]PercentileRange{ - 0.1: {Low: 50.0, High: 150.0}, - 0.25: {Low: 50.0, High: 150.0}, - 0.5: {Low: 50.0, High: 150.0}, - 0.75: {Low: 50.0, High: 150.0}, - 0.9: {Low: 50.0, High: 150.0}, - }, - }, - }, - { - Name: "Unbounded Histogram", - Input: HistogramInput{ - Count: 75, - Sum: 3500, - Min: nil, - Max: nil, - Boundaries: []float64{}, - Counts: []uint64{75}, - Attributes: map[string]string{"service.name": "unbounded-service"}, - }, - Expected: ExpectedMetrics{ - Count: 75, - Sum: 3500, - Average: 46.67, - Min: nil, - Max: nil, - PercentileRanges: map[float64]PercentileRange{ - 0.1: {Low: math.Inf(-1), High: math.Inf(1)}, - 0.25: {Low: math.Inf(-1), High: math.Inf(1)}, - 0.5: {Low: math.Inf(-1), High: math.Inf(1)}, - 0.75: {Low: math.Inf(-1), High: math.Inf(1)}, - 0.9: {Low: math.Inf(-1), High: math.Inf(1)}, - }, - }, - }, - // >100 buckets will be used for testing request splitting in PMD path - { - Name: "126 Buckets", - Input: HistogramInput{ - Count: 1386, // 126 buckets * 11 items each - Sum: 870555, - Min: ptr(5.0), - Max: ptr(1300.0), - Boundaries: boundaries125, - Counts: counts125, - Attributes: map[string]string{"service.name": "many-buckets-125"}, - }, - Expected: ExpectedMetrics{ - Count: 1386, - Sum: 870555, - Average: 573.14, - Min: ptr(5.0), - Max: ptr(1300.0), - PercentileRanges: map[float64]PercentileRange{ - 0.1: {Low: 120.0, High: 130.0}, - 0.25: {Low: 310.0, High: 320.0}, - 0.5: {Low: 630.0, High: 640.0}, - 0.75: {Low: 940.0, High: 950.0}, - 0.9: {Low: 1130.0, High: 1140.0}, - }, - }, - }, - // >150 buckets will be used for testing request splitting in EMF path - { - Name: "176 Buckets", - Input: HistogramInput{ - Count: 1936, // 176 buckets * 11 items each - Sum: 1697000, - Min: ptr(5.0), - Max: ptr(1800.0), - Boundaries: boundaries175, - Counts: counts175, - Attributes: map[string]string{"service.name": "many-buckets-175"}, - }, - Expected: ExpectedMetrics{ - Count: 1936, - Sum: 1697000, - Average: 804.23, - Min: ptr(5.0), - Max: ptr(1800.0), - PercentileRanges: map[float64]PercentileRange{ - 0.1: {Low: 170.0, High: 180.0}, - 0.25: {Low: 440.0, High: 450.0}, - 0.5: {Low: 880.0, High: 890.0}, - 0.75: {Low: 1320.0, High: 1330.0}, - 0.9: {Low: 1580.0, High: 1590.0}, - }, - }, - }, - // PMD should split into 3 requests - // EMF should split into 2 requests - { - Name: "225 Buckets", - Input: HistogramInput{ - Count: 2486, // 226 buckets * 11 items each - Sum: 2803750, - Min: ptr(5.0), - Max: ptr(2300.0), - Boundaries: boundaries225, - Counts: counts225, - Attributes: map[string]string{"service.name": "many-buckets-225"}, - }, - Expected: ExpectedMetrics{ - Count: 2486, - Sum: 2803750, - Average: 1027.25, - Min: ptr(5.0), - Max: ptr(2300.0), - PercentileRanges: map[float64]PercentileRange{ - 0.1: {Low: 220.0, High: 230.0}, - 0.25: {Low: 560.0, High: 570.0}, - 0.5: {Low: 1130.0, High: 1140.0}, - 0.75: {Low: 1690.0, High: 1700.0}, - 0.9: {Low: 2030.0, High: 2040.0}, - }, - }, - }, - // PMD should split into 4 requests - // EMF should split into 3 requests - { - Name: "325 Buckets", - Input: HistogramInput{ - Count: 3586, // 326 buckets * 11 items each - Sum: 5830500, - Min: ptr(5.0), - Max: ptr(3300.0), - Boundaries: boundaries325, - Counts: counts325, - Attributes: map[string]string{"service.name": "many-buckets-325"}, - }, - Expected: ExpectedMetrics{ - Count: 3586, - Sum: 5830500, - Average: 1486.47, - Min: ptr(5.0), - Max: ptr(3300.0), - PercentileRanges: map[float64]PercentileRange{ - 0.1: {Low: 320.0, High: 330.0}, - 0.25: {Low: 810.0, High: 820.0}, - 0.5: {Low: 1630.0, High: 1640.0}, - 0.75: {Low: 2440.0, High: 2450.0}, - 0.9: {Low: 2930.0, High: 2940.0}, - }, - }, - }, - } -} - -func InvalidTestCases() []HistogramTestCase { - return []HistogramTestCase{ - { - Name: "Boundaries Not Ascending", - Input: HistogramInput{ - Count: 100, - Sum: 5000, - Min: ptr(10.0), - Max: ptr(200.0), - Boundaries: []float64{25, 50, 40, 100, 150}, // 40 < 50 - Counts: []uint64{20, 30, 25, 15, 8, 2}, - Attributes: map[string]string{"service.name": "invalid-boundaries"}, - }, - Expected: ExpectedMetrics{}, - }, - { - Name: "Counts Length Mismatch", - Input: HistogramInput{ - Count: 100, - Sum: 5000, - Min: ptr(10.0), - Max: ptr(200.0), - Boundaries: []float64{25, 50, 75, 100}, - Counts: []uint64{20, 30, 25, 15, 8, 2}, // Should be 5 counts for 4 boundaries - Attributes: map[string]string{"service.name": "wrong-counts"}, - }, - Expected: ExpectedMetrics{}, - }, - { - Name: "Total Count Mismatch", - Input: HistogramInput{ - Count: 90, // Doesn't match sum of counts (100) - Sum: 5000, - Min: ptr(10.0), - Max: ptr(200.0), - Boundaries: []float64{25, 50, 75, 100, 150}, - Counts: []uint64{20, 30, 25, 15, 8, 2}, - Attributes: map[string]string{"service.name": "count-mismatch"}, - }, - Expected: ExpectedMetrics{}, - }, - { - Name: "Min Greater Than First Boundary", - Input: HistogramInput{ - Count: 100, - Sum: 5000, - Min: ptr(30.0), // Greater than first boundary (25) - Max: ptr(200.0), - Boundaries: []float64{25, 50, 75, 100, 150}, - Counts: []uint64{20, 30, 25, 15, 8, 2}, // Has counts in first bucket - Attributes: map[string]string{"service.name": "invalid-min"}, - }, - Expected: ExpectedMetrics{}, - }, - { - Name: "Max Less Than Last Boundary", - Input: HistogramInput{ - Count: 100, - Sum: 5000, - Min: ptr(10.0), - Max: ptr(140.0), // Less than last boundary (150) - Boundaries: []float64{25, 50, 75, 100, 150}, - Counts: []uint64{20, 30, 25, 15, 8, 2}, // Has counts in overflow bucket - Attributes: map[string]string{"service.name": "invalid-max"}, - }, - Expected: ExpectedMetrics{}, - }, - { - Name: "Sum Too Small", - Input: HistogramInput{ - Count: 100, - Sum: 100, // Too small given the boundaries and counts - Min: ptr(10.0), - Max: ptr(200.0), - Boundaries: []float64{25, 50, 75, 100, 150}, - Counts: []uint64{20, 30, 25, 15, 8, 2}, - Attributes: map[string]string{"service.name": "small-sum"}, - }, - Expected: ExpectedMetrics{}, - }, - { - Name: "Sum Too Large", - Input: HistogramInput{ - Count: 100, - Sum: 1000000, // Too large given the boundaries and counts - Min: ptr(10.0), - Max: ptr(200.0), - Boundaries: []float64{25, 50, 75, 100, 150}, - Counts: []uint64{20, 30, 25, 15, 8, 2}, - Attributes: map[string]string{"service.name": "large-sum"}, - }, - Expected: ExpectedMetrics{}, - }, - { - Name: "Min in Second Bucket But Sum Too Low", - Input: HistogramInput{ - Count: 100, - Sum: 2000, // This sum is too low given min is in second bucket - Min: ptr(60.0), // Min falls in second bucket (50,75] - Max: ptr(200.0), - Boundaries: []float64{50, 75, 100, 150}, - Counts: []uint64{20, 30, 25, 15, 10}, // 30 values must be at least 60 each in second bucket - Attributes: map[string]string{"service.name": "invalid-min-bucket"}, - }, - Expected: ExpectedMetrics{}, - }, - { - Name: "Max in Second-to-Last Bucket But Sum Too High", - Input: HistogramInput{ - Count: 100, - Sum: 10000, // This sum is too high given max is in second-to-last bucket - Min: ptr(10.0), - Max: ptr(90.0), // Max falls in second-to-last bucket (75,100] - Boundaries: []float64{50, 75, 100, 150}, - Counts: []uint64{20, 30, 25, 15, 10}, // No value can exceed 90 - Attributes: map[string]string{"service.name": "invalid-max-bucket"}, - }, - Expected: ExpectedMetrics{}, - }, - } -} - -func ptr(f float64) *float64 { - return &f -} diff --git a/share/testdata/histograms/histograms_test.go b/share/testdata/histograms/histograms_test.go deleted file mode 100644 index 3ab9d1f15acff..0000000000000 --- a/share/testdata/histograms/histograms_test.go +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT - -package histograms - -import ( - "fmt" - "math" - "regexp" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestHistogramFeasibility(t *testing.T) { - testCases := TestCases() - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - feasible, reason := checkFeasibility(tc.Input) - assert.True(t, feasible, reason) - - // check that the test case percentile ranges are valid - for percentile, expectedRange := range tc.Expected.PercentileRanges { - calculatedLow, calculatedHigh := calculatePercentileRange(tc.Input, percentile) - assert.Equal(t, expectedRange.Low, calculatedLow, "calculated low does not match expected low for percentile %v", percentile) - assert.Equal(t, expectedRange.High, calculatedHigh, "calculated high does not match expected high for percentile %v", percentile) - } - - assertOptionalFloat(t, "min", tc.Expected.Min, tc.Input.Min) - assertOptionalFloat(t, "max", tc.Expected.Max, tc.Input.Max) - }) - } -} - -func TestInvalidHistogramFeasibility(t *testing.T) { - invalidTestCases := InvalidTestCases() - - for _, tc := range invalidTestCases { - t.Run(tc.Name, func(t *testing.T) { - feasible, reason := checkFeasibility(tc.Input) - assert.False(t, feasible, reason) - }) - } -} - -func TestVisualizeHistograms(t *testing.T) { - // comment the next line to visualize the input histograms - //t.Skip("Skip visualization test") - testCases := TestCases() - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - // The large bucket tests are just too big to output - if matched, _ := regexp.MatchString("\\d\\d\\d Buckets", tc.Name); matched { - return - } - visualizeHistogramWithPercentiles(tc.Input) - }) - } -} - -func checkFeasibility(hi HistogramInput) (bool, string) { - - // Special case: empty histogram is valid - if len(hi.Boundaries) == 0 && len(hi.Counts) == 0 { - return true, "" - } - - // Check counts length matches boundaries + 1 - if len(hi.Counts) != len(hi.Boundaries)+1 { - return false, "Can't have counts without boundaries" - } - - if hi.Max != nil && hi.Min != nil && *hi.Min > *hi.Max { - return false, fmt.Sprintf("min %f is greater than max %f", *hi.Min, *hi.Max) - } - - // Rest of checks only apply if we have boundaries/counts - if len(hi.Boundaries) > 0 || len(hi.Counts) > 0 { - // Check boundaries are in ascending order - for i := 1; i < len(hi.Boundaries); i++ { - if hi.Boundaries[i] <= hi.Boundaries[i-1] { - return false, fmt.Sprintf("boundaries not in ascending order: %v <= %v", - hi.Boundaries[i], hi.Boundaries[i-1]) - } - } - - // Check counts array length - if len(hi.Counts) != len(hi.Boundaries)+1 { - return false, fmt.Sprintf("counts length (%d) should be boundaries length (%d) + 1", - len(hi.Counts), len(hi.Boundaries)) - } - - // Verify total count matches sum of bucket counts - var totalCount uint64 - for _, count := range hi.Counts { - totalCount += count - } - if totalCount != hi.Count { - return false, fmt.Sprintf("sum of counts (%d) doesn't match total count (%d)", - totalCount, hi.Count) - } - - // Check min/max feasibility if defined - if hi.Min != nil { - // If there are boundaries, first bucket must have counts > 0 only if min <= first boundary - if len(hi.Boundaries) > 0 && hi.Counts[0] > 0 && *hi.Min > hi.Boundaries[0] { - return false, fmt.Sprintf("min (%v) > first boundary (%v) but first bucket has counts", - *hi.Min, hi.Boundaries[0]) - } - } - - if hi.Max != nil { - // If there are boundaries, last bucket must have counts > 0 only if max > last boundary - if len(hi.Boundaries) > 0 && hi.Counts[len(hi.Counts)-1] > 0 && - *hi.Max <= hi.Boundaries[len(hi.Boundaries)-1] { - return false, fmt.Sprintf("max (%v) <= last boundary (%v) but overflow bucket has counts", - *hi.Max, hi.Boundaries[len(hi.Boundaries)-1]) - } - } - - // Check sum feasibility - if len(hi.Boundaries) > 0 { - // Calculate minimum possible sum - minSum := float64(0) - if hi.Min != nil { - // Find which bucket the minimum value belongs to - minBucket := 0 - for i, bound := range hi.Boundaries { - if *hi.Min > bound { - minBucket = i + 1 - } - } - // Apply min value only from its containing bucket - for i := minBucket; i < len(hi.Counts); i++ { - if i == minBucket { - minSum += float64(hi.Counts[i]) * *hi.Min - } else { - minSum += float64(hi.Counts[i]) * hi.Boundaries[i-1] - } - } - } else { - // Without min, use lower bounds - for i := 1; i < len(hi.Counts); i++ { - minSum += float64(hi.Counts[i]) * hi.Boundaries[i-1] - } - } - - // Calculate maximum possible sum - maxSum := float64(0) - if hi.Max != nil { - // Find which bucket the maximum value belongs to - maxBucket := len(hi.Boundaries) // Default to overflow bucket - for i, bound := range hi.Boundaries { - if *hi.Max <= bound { - maxBucket = i - break - } - } - // Apply max value only up to its containing bucket - for i := 0; i < len(hi.Counts); i++ { - if i > maxBucket { - maxSum += float64(hi.Counts[i]) * *hi.Max - } else if i == len(hi.Boundaries) { - maxSum += float64(hi.Counts[i]) * *hi.Max - } else { - maxSum += float64(hi.Counts[i]) * hi.Boundaries[i] - } - } - } else { - // If no max defined, we can't verify upper bound - maxSum = math.Inf(1) - } - - if hi.Sum < minSum { - return false, fmt.Sprintf("sum (%v) is less than minimum possible sum (%v)", - hi.Sum, minSum) - } - if maxSum != math.Inf(1) && hi.Sum > maxSum { - return false, fmt.Sprintf("sum (%v) is greater than maximum possible sum (%v)", - hi.Sum, maxSum) - } - } - } - - return true, "" -} - -func calculatePercentileRange(hi HistogramInput, percentile float64) (float64, float64) { - if len(hi.Boundaries) == 0 { - // No buckets - use min/max if available - if hi.Min != nil && hi.Max != nil { - return *hi.Min, *hi.Max - } - return math.Inf(-1), math.Inf(1) - } - - percentilePosition := uint64(float64(hi.Count) * percentile) - var cumulativeCount uint64 - - // Find which bucket contains the percentile - for i, count := range hi.Counts { - cumulativeCount += count - if cumulativeCount > percentilePosition { - // Found the bucket containing the percentile - if i == 0 { - // First bucket: (-inf, bounds[0]] - if hi.Min != nil { - return *hi.Min, hi.Boundaries[0] - } - return math.Inf(-1), hi.Boundaries[0] - } else if i == len(hi.Boundaries) { - // Last bucket: (bounds[last], +inf) - if hi.Max != nil { - return hi.Boundaries[i-1], *hi.Max - } - return hi.Boundaries[i-1], math.Inf(1) - } else { - // Middle bucket: (bounds[i-1], bounds[i]] - return hi.Boundaries[i-1], hi.Boundaries[i] - } - } - } - return 0, 0 // Should never reach here for valid histograms -} - -func assertOptionalFloat(t *testing.T, name string, expected, actual *float64) { - if expected != nil { - assert.NotNil(t, actual, "Expected %s defined but not defined on input", name) - if actual != nil { - assert.Equal(t, expected, actual) - } - } else { - assert.Nil(t, actual, "Input %s defined but no %s is expected", name, name) - } -} - -func visualizeHistogramWithPercentiles(hi HistogramInput) { - fmt.Printf("\nHistogram Visualization with Percentiles\n") - fmt.Printf("Count: %d, Sum: %.2f\n", hi.Count, hi.Sum) - if hi.Min != nil { - fmt.Printf("Min: %.2f ", *hi.Min) - } - if hi.Max != nil { - fmt.Printf("Max: %.2f", *hi.Max) - } - fmt.Println() - - if len(hi.Boundaries) == 0 { - fmt.Println("No buckets defined") - return - } - - // Calculate cumulative counts for CDF - cumulativeCounts := make([]uint64, len(hi.Counts)) - var total uint64 - for i, count := range hi.Counts { - total += count - cumulativeCounts[i] = total - } - - // Find percentile positions - percentiles := []float64{0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99} - percentilePositions := make(map[float64]int) - for _, p := range percentiles { - pos := uint64(float64(hi.Count) * p) - for i, cumCount := range cumulativeCounts { - if cumCount > pos { - percentilePositions[p] = i - break - } - } - } - - maxCount := uint64(0) - for _, count := range hi.Counts { - if count > maxCount { - maxCount = count - } - } - - fmt.Println("\nHistogram:") - for i, count := range hi.Counts { - var bucketLabel string - if i == 0 { - if hi.Min != nil { - bucketLabel = fmt.Sprintf("(%.2f, %.1f]", *hi.Min, hi.Boundaries[0]) - } else { - bucketLabel = fmt.Sprintf("(-∞, %.1f]", hi.Boundaries[0]) - } - } else if i == len(hi.Boundaries) { - if hi.Max != nil { - bucketLabel = fmt.Sprintf("(%.1f, %.2f]", hi.Boundaries[i-1], *hi.Max) - } else { - bucketLabel = fmt.Sprintf("(%.1f, +∞)", hi.Boundaries[i-1]) - } - } else { - bucketLabel = fmt.Sprintf("(%.1f, %.1f]", hi.Boundaries[i-1], hi.Boundaries[i]) - } - - barLength := int(float64(count) / float64(maxCount) * 40) - bar := strings.Repeat("█", barLength) - - // Mark percentile buckets - percentileMarkers := "" - for _, p := range percentiles { - if percentilePositions[p] == i { - percentileMarkers += fmt.Sprintf(" P%.0f", p*100) - } - } - - fmt.Printf("%-30s %4d |%s%s\n", bucketLabel, count, bar, percentileMarkers) - } - - fmt.Println("\nCumulative Distribution (CDF):") - for i, cumCount := range cumulativeCounts { - var bucketLabel string - if i == 0 { - bucketLabel = fmt.Sprintf("≤ %.1f", hi.Boundaries[0]) - } else if i == len(hi.Boundaries) { - bucketLabel = "≤ +∞" - } else { - bucketLabel = fmt.Sprintf("≤ %.1f", hi.Boundaries[i]) - } - - cdfPercent := float64(cumCount) / float64(hi.Count) * 100 - cdfBarLength := int(cdfPercent / 100 * 40) - cdfBar := strings.Repeat("▓", cdfBarLength) - - // Add percentile lines - percentileLines := "" - for _, p := range percentiles { - if percentilePositions[p] == i { - percentileLines += fmt.Sprintf(" ──P%.0f", p*100) - } - } - - fmt.Printf("%-15s %6.1f%% |%s%s\n", bucketLabel, cdfPercent, cdfBar, percentileLines) - } - - // Show percentile ranges - fmt.Println("\nPercentile Ranges:") - for _, p := range percentiles { - low, high := calculatePercentileRange(hi, p) - fmt.Printf("P%.0f: [%.2f, %.2f]\n", p*100, low, high) - } -} From 305b417f9a4febd80e7b48e4d3fe8d03936a97be Mon Sep 17 00:00:00 2001 From: okankoAMZ <107267850+okankoAMZ@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:32:35 -0700 Subject: [PATCH 22/23] Add OpenTelemetry Histogram Generator (#366) feat: implement statistical histogram generator with OpenTelemetry integration - Add comprehensive statistical distribution support: - Gamma, Weibull, LogNormal, Beta, and Exponential distributions - Sinusoidal, spiky, and trending value generators - Realistic metric patterns (CPU cycles, memory leaks, response times) - Integrate with OpenTelemetry: - Replace manual OTLP with official telemetrygen package - Add conversion utilities for percentile ranges - Implement dynamic histogram generation capabilities - Improve code organization: - Split generator into focused modules (types, distributions, publisher) - Follow single-responsibility design principles - Add comprehensive documentation and examples - Include tests and usage examples This implements a complete histogram generation system for realistic OpenTelemetry test data with configurable statistical distributions. --- cmd/generator/README.md | 147 +++++++ cmd/generator/distributions.go | 92 ++++ cmd/generator/example_test.go | 632 +++++++++++++++++++++++++++ cmd/generator/generator.go | 33 ++ cmd/generator/go.sum | 53 +++ cmd/generator/histogram_generator.go | 222 ++++++++++ cmd/generator/otlp_publisher.go | 74 ++++ cmd/generator/types.go | 43 ++ 8 files changed, 1296 insertions(+) create mode 100644 cmd/generator/README.md create mode 100644 cmd/generator/distributions.go create mode 100644 cmd/generator/example_test.go create mode 100644 cmd/generator/generator.go create mode 100644 cmd/generator/histogram_generator.go create mode 100644 cmd/generator/otlp_publisher.go create mode 100644 cmd/generator/types.go diff --git a/cmd/generator/README.md b/cmd/generator/README.md new file mode 100644 index 0000000000000..6cf0bab8684de --- /dev/null +++ b/cmd/generator/README.md @@ -0,0 +1,147 @@ +# Histogram Generator + +A Go package for generating realistic histogram data for OpenTelemetry metrics testing. This package provides statistical distribution functions and can optionally publish generated metrics to OTLP endpoints. + +## Architecture + +The package is organized into focused modules for maintainability and scalability: + +``` +cmd/generator/ +├── generator.go # Package documentation and overview +├── types.go # Core data structures and types +├── histogram_generator.go # Main histogram generation logic +├── distributions.go # Statistical distribution functions +├── otlp_publisher.go # OTLP endpoint publishing functionality +└── example_test.go # Usage examples +``` + +### Key Components + +- **HistogramGenerator**: Main generator that creates histogram data from statistical distributions +- **OTLPPublisher**: Handles publishing metrics to OpenTelemetry Protocol endpoints using both telemetrygen and custom OTLP +- **Distribution Functions**: Various statistical distributions (Normal, Exponential, Gamma, etc.) +- **Types**: Shared data structures for histogram inputs, outputs, and configuration + +### Publishing Approach + +The package uses the official OpenTelemetry telemetrygen package for all metric publishing, ensuring: + +- **Standard Compliance**: Full compatibility with OTLP endpoints +- **Reliability**: Uses the same code as the official telemetrygen tool +- **Simplicity**: Clean API with `metrics.Start(cfg)` under the hood +- **Flexibility**: Supports Gauge, Sum, and Histogram metric types + +The histogram generator creates realistic data distributions, while telemetrygen handles the actual OTLP publishing. + +## Usage + +### Basic Generation + +```go +generator := NewHistogramGenerator(GenerationOptions{ + Seed: 12345, // For reproducible results +}) + +input := HistogramInput{ + Count: 1000, + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Attributes: map[string]string{"service.name": "test-service"}, +} + +result, err := generator.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return NormalRandom(rnd, 75, 25) // mean=75, stddev=25 +}) +``` + +### Generation with Publishing + +```go +generator := NewHistogramGenerator(GenerationOptions{ + Seed: time.Now().UnixNano(), + Endpoint: "localhost:4318", // OTLP HTTP endpoint +}) + +result, err := generator.GenerateAndPublishHistograms(input, valueFunc) +``` + +### Direct Publishing with Telemetrygen + +You can also use telemetrygen directly for different metric types: + +```go +publisher := NewOTLPPublisher("localhost:4318") + +// Send different types of metrics using telemetrygen +err := publisher.SendSumMetric("requests_total", 100) +err = publisher.SendGaugeMetric("cpu_usage", 75.5) +err = publisher.SendHistogramMetricSimple("response_time") +``` + +This approach uses the official OpenTelemetry telemetrygen package under the hood, ensuring compatibility with standard OTLP endpoints. + +## Available Distributions + +- **NormalRandom**: Normal (Gaussian) distribution +- **ExponentialRandom**: Exponential distribution +- **GammaRandom**: Gamma distribution +- **LogNormalRandom**: Log-normal distribution +- **WeibullRandom**: Weibull distribution +- **BetaRandom**: Beta distribution + +### Time-based Functions + +- **SinusoidalValue**: Sinusoidal patterns with noise +- **SpikyValue**: Baseline with occasional spikes +- **TrendingValue**: Linear trend with noise + +## Design Principles + +### Single Responsibility +Each file has a focused purpose: +- `types.go`: Data structures only +- `distributions.go`: Statistical functions only +- `histogram_generator.go`: Core generation logic only +- `otlp_publisher.go`: Publishing logic only + +### Dependency Injection +The generator accepts value functions, allowing for flexible distribution selection and custom patterns. + +### Testability +All components are designed for easy unit testing with deterministic seeds and dependency injection. + +### Extensibility +New distributions can be added to `distributions.go` without affecting other components. + +## Features + +- ✅ **Statistical Distributions**: Multiple distribution functions for realistic data +- ✅ **OTLP Publishing**: Direct integration with OpenTelemetry Protocol endpoints +- ✅ **Flexible Generation**: Custom value functions and deterministic seeds +- ✅ **Multiple Metric Types**: Support for Gauge, Sum, and Histogram metrics + +## Future Enhancements + +1. **Additional Publishers**: Support for Prometheus, StatsD, etc. +2. **More Distributions**: Poisson, Binomial, etc. +3. **Validation**: Input validation for histogram consistency +4. **Batch Generation**: Generate multiple histograms efficiently +5. **Configuration Files**: YAML/JSON configuration support + +## Testing + +The package includes comprehensive examples in `example_test.go` and integrates with the test cases in `share/testdata/histograms/`. + +Run tests: +```bash +go test ./cmd/generator/... +``` + +## Integration + +This generator is used by: +- `share/testdata/histograms/histograms.go`: Test case generation +- Various metric exporters for testing realistic data patterns +- Performance testing tools for load generation \ No newline at end of file diff --git a/cmd/generator/distributions.go b/cmd/generator/distributions.go new file mode 100644 index 0000000000000..55144d10f3979 --- /dev/null +++ b/cmd/generator/distributions.go @@ -0,0 +1,92 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "math" + "math/rand" + "time" +) + +// Distribution functions for generating statistical data + +func ExponentialRandom(rnd *rand.Rand, rate float64) float64 { + return -math.Log(1.0-rnd.Float64()) / rate +} + +func NormalRandom(rnd *rand.Rand, mean, stddev float64) float64 { + return rnd.NormFloat64()*stddev + mean +} + +func LogNormalRandom(rnd *rand.Rand, mu, sigma float64) float64 { + return math.Exp(NormalRandom(rnd, mu, sigma)) +} + +func WeibullRandom(rnd *rand.Rand, shape, scale float64) float64 { + return scale * math.Pow(-math.Log(1.0-rnd.Float64()), 1.0/shape) +} + +func BetaRandom(rnd *rand.Rand, alpha, beta float64) float64 { + x := GammaRandom(rnd, alpha, 1.0) + y := GammaRandom(rnd, beta, 1.0) + return x / (x + y) +} + +func GammaRandom(rnd *rand.Rand, alpha, beta float64) float64 { + if alpha < 1.0 { + // Use Johnk's generator for alpha < 1 + for { + u := rnd.Float64() + v := rnd.Float64() + x := math.Pow(u, 1.0/alpha) + y := math.Pow(v, 1.0/(1.0-alpha)) + if x+y <= 1.0 { + if x+y > 0 { + return beta * x / (x + y) * (-math.Log(rnd.Float64())) + } + } + } + } + + // Marsaglia and Tsang's method for alpha >= 1 + d := alpha - 1.0/3.0 + c := 1.0 / math.Sqrt(9.0*d) + + for { + x := rnd.NormFloat64() + v := 1.0 + c*x + if v <= 0 { + continue + } + v = v * v * v + u := rnd.Float64() + if u < 1.0-0.0331*(x*x)*(x*x) { + return beta * d * v + } + if math.Log(u) < 0.5*x*x+d*(1.0-v+math.Log(v)) { + return beta * d * v + } + } +} + +// Time-based value functions + +func SinusoidalValue(rnd *rand.Rand, timestamp time.Time, amplitude, period, phase, baseline float64) float64 { + t := float64(timestamp.Unix()) + noise := rnd.NormFloat64() * amplitude * 0.1 // 10% noise + return baseline + amplitude*math.Sin(2*math.Pi*t/period+phase) + noise +} + +func SpikyValue(rnd *rand.Rand, baseline, spikeHeight, spikeProb float64) float64 { + if rnd.Float64() < spikeProb { + return baseline + spikeHeight*rnd.Float64() + } + return baseline + rnd.NormFloat64()*baseline*0.1 +} + +func TrendingValue(rnd *rand.Rand, timestamp time.Time, startValue, trendRate, noise float64) float64 { + t := float64(timestamp.Unix()) + trend := startValue + trendRate*t + return trend + rnd.NormFloat64()*noise +} diff --git a/cmd/generator/example_test.go b/cmd/generator/example_test.go new file mode 100644 index 0000000000000..47064a3656f19 --- /dev/null +++ b/cmd/generator/example_test.go @@ -0,0 +1,632 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package generator_test + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/amazon-contributing/opentelemetry-collector-contrib/cmd/generator" +) + +// TestHistogramGenerator_GenerateHistogram_Example demonstrates basic histogram generation +// CURRENT CAPABILITY: Statistical histogram data generation with custom distributions +func TestHistogramGenerator_GenerateHistogram_Example(t *testing.T) { + fmt.Println("=== CURRENT GOAL: Statistical Histogram Data Generation ===") + + // Create a generator with a fixed seed for reproducible results + gen := generator.NewHistogramGenerator(generator.GenerationOptions{ + Seed: 12345, + }) + + // Define histogram input parameters + input := generator.HistogramInput{ + Count: 1000, + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Attributes: map[string]string{ + "service.name": "payment-service", + "environment": "production", + }, + } + + // Generate histogram using normal distribution + result, err := gen.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 25) // mean=75, stddev=25 + }) + + if err != nil { + panic(err) + } + + fmt.Printf("✅ Generated %d samples with sum=%.2f, avg=%.2f\n", + result.Expected.Count, result.Expected.Sum, result.Expected.Average) + fmt.Printf("✅ Min=%.2f, Max=%.2f\n", *result.Expected.Min, *result.Expected.Max) + fmt.Printf("✅ Bucket distribution: %v\n", result.Input.Counts) + + // This example demonstrates histogram generation with statistical distributions. + // Output will vary due to randomness, but structure is consistent. +} + +// TestHieHistogrenerator_GenerateAndPublishHistograms_Example shows telemetrygen integration +// CURRENT LIMITATION: Only supports basic histogram publishing, not custom bucket data +func TestHistogramGenerator_GenerateAndPublishHistograms_Example(t *testing.T) { + // Make sure collector is running before removing the skip on this function + t.Skip() + fmt.Println("=== CURRENT GOAL: Basic OTLP Publishing via Telemetrygen ===") + + // Create a generator with OTLP endpoint + gen := generator.NewHistogramGenerator(generator.GenerationOptions{ + Seed: time.Now().UnixNano(), + Endpoint: "localhost:4318", // OTLP HTTP endpoint + }) + + input := generator.HistogramInput{ + Count: 500, + Boundaries: []float64{10, 50, 100, 500, 1000}, + Attributes: map[string]string{ + "service.name": "web-service", + "service.version": "1.0.0", + "environment": "staging", + }, + } + + // Generate and publish using exponential distribution + result, err := gen.GenerateAndPublishHistograms(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.ExponentialRandom(rnd, 0.01) // rate=0.01 + }) + + if err != nil { + fmt.Printf("❌ Error (expected - telemetrygen limitations): %v\n", err) + return + } + + fmt.Printf("✅ Generated and published histogram with %d samples\n", result.Expected.Count) + fmt.Printf("⚠️ Note: Uses telemetrygen's built-in histogram generation, not custom buckets\n") +} + +// TestOTLPPublisher_SendSumMetric_Example demonstrates current telemetrygen integration +// CURRENT CAPABILITY: Basic Sum, Gauge, Histogram via telemetrygen +// MISSING: Delta/Cumulative distinction, Summary, Exponential Histogram +func TestOTLPPublisher_SendSumMetric_Example(t *testing.T) { + // Make sure collector is running before removing the skip on this function + t.Skip() + fmt.Println("=== CURRENT GOAL: Basic Metric Types via Telemetrygen ===") + + publisher := generator.NewOTLPPublisher("localhost:4318") + + // ✅ WORKING: Basic metric types supported by telemetrygen + err := publisher.SendSumMetric("requests_total", 100) + if err != nil { + fmt.Printf("❌ Error sending sum metric: %v\n", err) + return + } + fmt.Println("✅ Sum metric sent (telemetrygen)") + + err = publisher.SendGaugeMetric("cpu_usage", 75.5) + if err != nil { + fmt.Printf("❌ Error sending gauge metric: %v\n", err) + return + } + fmt.Println("✅ Gauge metric sent (telemetrygen)") + + err = publisher.SendHistogramMetricSimple("response_time") + if err != nil { + fmt.Printf("❌ Error sending histogram metric: %v\n", err) + return + } + fmt.Println("✅ Basic histogram sent (telemetrygen)") + + fmt.Println("\n⚠️ LIMITATIONS:") + fmt.Println(" - No delta vs cumulative temporality control") + fmt.Println(" - No summary metrics") + fmt.Println(" - No exponential histograms") + fmt.Println(" - No custom histogram bucket data") +} + +// TestAllDistributions_Example demonstrates all available statistical distributions +// CURRENT CAPABILITY: Complete statistical distribution library +func TestAllDistributions_Example(t *testing.T) { + fmt.Println("=== CURRENT CAPABILITY: All Statistical Distributions ===") + + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 42}) + boundaries := []float64{10, 25, 50, 75, 100, 150, 200} + attributes := map[string]string{"test": "distributions"} + + fmt.Println("\n📊 PROBABILITY DISTRIBUTIONS:") + + // Normal Distribution + result, _ := gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 20) // mean=75, stddev=20 + }) + fmt.Printf("✅ Normal (μ=75, σ=20): avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) + + // Exponential Distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.ExponentialRandom(rnd, 0.02) // rate=0.02 + }) + fmt.Printf("✅ Exponential (λ=0.02): avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) + + // Log-Normal Distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.LogNormalRandom(rnd, 3.5, 0.8) // mu=3.5, sigma=0.8 + }) + fmt.Printf("✅ Log-Normal (μ=3.5, σ=0.8): avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) + + // Gamma Distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.GammaRandom(rnd, 2.0, 25.0) // shape=2, scale=25 + }) + fmt.Printf("✅ Gamma (α=2, β=25): avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) + + // Weibull Distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.WeibullRandom(rnd, 2.5, 80) // shape=2.5, scale=80 + }) + fmt.Printf("✅ Weibull (k=2.5, λ=80): avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) + + // Beta Distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.BetaRandom(rnd, 2, 5) * 200 // scale to 0-200 range + }) + fmt.Printf("✅ Beta (α=2, β=5) scaled: avg=%.1f, samples=%d\n", + result.Expected.Average, result.Expected.Count) +} + +// TestTimeBasedPatterns_Example demonstrates time-based value generation +// CURRENT CAPABILITY: Dynamic time-based patterns for realistic metrics +func TestTimeBasedPatterns_Example(t *testing.T) { + fmt.Println("\n=== CURRENT CAPABILITY: Time-Based Patterns ===") + + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 123}) + boundaries := []float64{20, 40, 60, 80, 100, 120} + attributes := map[string]string{"pattern": "time-based"} + + fmt.Println("\n🕐 TIME-BASED FUNCTIONS:") + + // Sinusoidal Pattern (daily cycle) + result, _ := gen.GenerateHistogram(generator.HistogramInput{ + Count: 500, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.SinusoidalValue(rnd, t, 30, 86400, 0, 60) // 24-hour cycle + }) + fmt.Printf("✅ Sinusoidal (24h cycle): avg=%.1f, range=[%.1f-%.1f]\n", + result.Expected.Average, *result.Expected.Min, *result.Expected.Max) + + // Spiky Pattern (occasional bursts) + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 500, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.SpikyValue(rnd, 50, 100, 0.1) // 10% spike probability + }) + fmt.Printf("✅ Spiky (10%% spikes): avg=%.1f, range=[%.1f-%.1f]\n", + result.Expected.Average, *result.Expected.Min, *result.Expected.Max) + + // Trending Pattern (gradual increase) + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 500, Boundaries: boundaries, Attributes: attributes, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.TrendingValue(rnd, t, 40, 0.0001, 8) // slow upward trend + }) + fmt.Printf("✅ Trending (upward): avg=%.1f, range=[%.1f-%.1f]\n", + result.Expected.Average, *result.Expected.Min, *result.Expected.Max) +} + +// TestAdvancedHistogramFeatures_Example shows advanced histogram generation capabilities +// CURRENT CAPABILITY: Custom buckets, attributes, statistical validation +func TestAdvancedHistogramFeatures_Example(t *testing.T) { + fmt.Println("\n=== CURRENT CAPABILITY: Advanced Histogram Features ===") + + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 999}) + + fmt.Println("\n🔧 ADVANCED FEATURES:") + + // Custom bucket boundaries for different use cases + latencyBoundaries := []float64{1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000} + sizeBoundaries := []float64{1024, 4096, 16384, 65536, 262144, 1048576, 4194304} + + // Latency histogram with log-normal distribution + result, _ := gen.GenerateHistogram(generator.HistogramInput{ + Count: 2000, + Min: ptr(0.5), + Max: ptr(10000.0), + Boundaries: latencyBoundaries, + Attributes: map[string]string{ + "service.name": "api-gateway", + "endpoint": "/users", + "method": "GET", + "status_code": "200", + }, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.LogNormalRandom(rnd, 3.0, 1.2) // realistic latency distribution + }) + fmt.Printf("✅ Latency histogram: %d samples, avg=%.1fms\n", + result.Expected.Count, result.Expected.Average) + fmt.Printf(" Buckets: %v\n", result.Input.Counts) + + // File size histogram with exponential distribution + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1500, + Boundaries: sizeBoundaries, + Attributes: map[string]string{ + "file_type": "image", + "compression": "jpeg", + "quality": "high", + }, + }, func(rnd *rand.Rand, t time.Time) float64 { + return generator.ExponentialRandom(rnd, 0.000001) // file size distribution + }) + fmt.Printf("✅ File size histogram: %d samples, avg=%.0f bytes\n", + result.Expected.Count, result.Expected.Average) + + // Multi-modal distribution (combining two normals) + result, _ = gen.GenerateHistogram(generator.HistogramInput{ + Count: 1000, + Boundaries: []float64{10, 30, 50, 70, 90, 110, 130, 150}, + Attributes: map[string]string{ + "distribution": "bimodal", + "use_case": "response_time", + }, + }, func(rnd *rand.Rand, t time.Time) float64 { + if rnd.Float64() < 0.7 { + return generator.NormalRandom(rnd, 40, 10) // fast responses (70%) + } + return generator.NormalRandom(rnd, 120, 15) // slow responses (30%) + }) + fmt.Printf("✅ Bimodal distribution: avg=%.1f (fast+slow responses)\n", + result.Expected.Average) +} + +// TestCurrentPublishingCapabilities_Example shows what publishing works today +// CURRENT CAPABILITY: Basic OTLP publishing via telemetrygen +func TestCurrentPublishingCapabilities_Example(t *testing.T) { + // Make sure collector is running before removing the skip on this function + t.Skip() + fmt.Println("\n=== CURRENT CAPABILITY: OTLP Publishing ===") + + publisher := generator.NewOTLPPublisher("localhost:4318") + + fmt.Println("\n📡 WORKING METRIC TYPES:") + + // These work with current telemetrygen integration + metrics := []struct { + name string + metricType string + value float64 + status string + }{ + {"http_requests_total", "Sum", 1500, "✅ Counter/Sum"}, + {"cpu_utilization", "Gauge", 67.8, "✅ Gauge"}, + {"response_time_histogram", "Histogram", 0, "✅ Basic Histogram"}, + {"memory_usage_bytes", "Gauge", 2147483648, "✅ Gauge (large values)"}, + {"error_rate", "Gauge", 0.025, "✅ Gauge (fractional)"}, + } + + for _, m := range metrics { + err := publisher.SendMetric(m.name, m.metricType, m.value) + if err != nil { + fmt.Printf("❌ %s: %v\n", m.status, err) + } else { + fmt.Printf("%s: %s (%.2f)\n", m.status, m.name, m.value) + } + } + + fmt.Println("\n⚠️ TELEMETRYGEN LIMITATIONS:") + fmt.Println(" - No custom histogram bucket data") + fmt.Println(" - No temporality control (delta vs cumulative)") + fmt.Println(" - No summary metrics") + fmt.Println(" - No exponential histograms") +} + +// TestShowCapabilities demonstrates all current capabilities +func TestShowCapabilities(t *testing.T) { + fmt.Println("\n🎯 RUNNING CAPABILITY DEMONSTRATION") + + // Run all the example functions to show capabilities + TestHistogramGenerator_GenerateHistogram_Example(t) + fmt.Println() + + // Skip publishing test that requires OTLP endpoint + fmt.Println("=== SKIPPING: OTLP Publishing (requires running collector) ===") + fmt.Println() + + TestAllDistributions_Example(t) + fmt.Println() + + TestTimeBasedPatterns_Example(t) + fmt.Println() + + TestAdvancedHistogramFeatures_Example(t) + fmt.Println() + + TestCurrentPublishingCapabilities_Example(t) + fmt.Println() + +} + +// TestHistogramGenerator_GenerateHistogram tests the core histogram generation functionality +func TestHistogramGenerator_GenerateHistogram(t *testing.T) { + gen := generator.NewHistogramGenerator(generator.GenerationOptions{ + Seed: 12345, // Fixed seed for reproducible tests + }) + + input := generator.HistogramInput{ + Count: 1000, + Min: ptr(10.0), + Max: ptr(200.0), + Boundaries: []float64{25, 50, 75, 100, 150}, + Attributes: map[string]string{ + "service.name": "test-service", + }, + } + + result, err := gen.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 25) // mean=75, stddev=25 + }) + + // Test basic functionality + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + // Test sample count + if result.Expected.Count != 1000 { + t.Errorf("Expected 1000 samples, got %d", result.Expected.Count) + } + + // Test min/max constraints + if result.Expected.Min == nil || *result.Expected.Min < 10.0 { + t.Errorf("Expected min >= 10.0, got %v", result.Expected.Min) + } + if result.Expected.Max == nil || *result.Expected.Max > 200.0 { + t.Errorf("Expected max <= 200.0, got %v", result.Expected.Max) + } + + // Test that average is reasonable for normal distribution (mean=75) + if result.Expected.Average < 60 || result.Expected.Average > 90 { + t.Errorf("Expected average around 75 (60-90), got %.2f", result.Expected.Average) + } + + // Test bucket counts + if len(result.Input.Counts) != len(input.Boundaries)+1 { + t.Errorf("Expected %d buckets, got %d", len(input.Boundaries)+1, len(result.Input.Counts)) + } + + // Test that sum equals count * average (approximately) + expectedSum := float64(result.Expected.Count) * result.Expected.Average + if abs(result.Expected.Sum-expectedSum) > 1.0 { + t.Errorf("Sum/average mismatch: sum=%.2f, count*avg=%.2f", result.Expected.Sum, expectedSum) + } + + // Test attributes are preserved + if result.Input.Attributes["service.name"] != "test-service" { + t.Errorf("Expected service.name=test-service, got %s", result.Input.Attributes["service.name"]) + } +} + +// TestHistogramGenerator_Distributions tests all statistical distributions +func TestHistogramGenerator_Distributions(t *testing.T) { + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 42}) + boundaries := []float64{10, 25, 50, 75, 100} + input := generator.HistogramInput{ + Count: 1000, Boundaries: boundaries, + } + + distributions := []struct { + name string + valueFunc func(*rand.Rand, time.Time) float64 + minAvg float64 + maxAvg float64 + }{ + { + name: "Normal", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 50, 10) + }, + minAvg: 40, maxAvg: 60, + }, + { + name: "Exponential", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.ExponentialRandom(rnd, 0.02) + }, + minAvg: 30, maxAvg: 70, + }, + { + name: "Gamma", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.GammaRandom(rnd, 2.0, 25.0) + }, + minAvg: 40, maxAvg: 60, + }, + } + + for _, dist := range distributions { + t.Run(dist.name, func(t *testing.T) { + result, err := gen.GenerateHistogram(input, dist.valueFunc) + + if err != nil { + t.Fatalf("Distribution %s failed: %v", dist.name, err) + } + + if result.Expected.Count != 1000 { + t.Errorf("Distribution %s: expected 1000 samples, got %d", dist.name, result.Expected.Count) + } + + if result.Expected.Average < dist.minAvg || result.Expected.Average > dist.maxAvg { + t.Errorf("Distribution %s: average %.2f outside expected range [%.1f-%.1f]", + dist.name, result.Expected.Average, dist.minAvg, dist.maxAvg) + } + }) + } +} + +// TestOTLPPublisher_Creation tests OTLP publisher creation +func TestOTLPPublisher_Creation(t *testing.T) { + t.Skip() + publisher := generator.NewOTLPPublisher("localhost:4318") + + if publisher == nil { + t.Fatal("Expected publisher to be created, got nil") + } + + // Test that publisher has the expected endpoint (this would require exposing the field or adding a getter) + // For now, just test that it was created successfully +} + +// TestGenerationOptions tests the generation options +func TestGenerationOptions(t *testing.T) { + // Test with seed + gen1 := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 123}) + gen2 := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 123}) + + input := generator.HistogramInput{Count: 100, Boundaries: []float64{50, 100}} + + result1, err1 := gen1.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 10) + }) + + result2, err2 := gen2.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 { + return generator.NormalRandom(rnd, 75, 10) + }) + + if err1 != nil || err2 != nil { + t.Fatalf("Expected no errors, got %v, %v", err1, err2) + } + + // With same seed, results should be identical + if result1.Expected.Sum != result2.Expected.Sum { + t.Errorf("Expected identical sums with same seed, got %.2f vs %.2f", + result1.Expected.Sum, result2.Expected.Sum) + } +} + +// TestTimeBasedPatterns tests time-based value functions +func TestTimeBasedPatterns(t *testing.T) { + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 999}) + input := generator.HistogramInput{Count: 100, Boundaries: []float64{25, 50, 75, 100}} + + patterns := []struct { + name string + valueFunc func(*rand.Rand, time.Time) float64 + }{ + { + name: "Sinusoidal", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.SinusoidalValue(rnd, t, 20, 3600, 0, 50) + }, + }, + { + name: "Spiky", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.SpikyValue(rnd, 50, 100, 0.1) + }, + }, + { + name: "Trending", + valueFunc: func(rnd *rand.Rand, t time.Time) float64 { + return generator.TrendingValue(rnd, t, 40, 0.001, 5) + }, + }, + } + + for _, pattern := range patterns { + t.Run(pattern.name, func(t *testing.T) { + result, err := gen.GenerateHistogram(input, pattern.valueFunc) + + if err != nil { + t.Fatalf("Pattern %s failed: %v", pattern.name, err) + } + + if result.Expected.Count != 100 { + t.Errorf("Pattern %s: expected 100 samples, got %d", pattern.name, result.Expected.Count) + } + + // Test that we got reasonable values (not all zeros or infinities) + if result.Expected.Sum <= 0 || result.Expected.Average <= 0 { + t.Errorf("Pattern %s: got unreasonable values sum=%.2f, avg=%.2f", + pattern.name, result.Expected.Sum, result.Expected.Average) + } + }) + } +} + +// TestEdgeCases tests edge cases and error conditions +func TestEdgeCases(t *testing.T) { + gen := generator.NewHistogramGenerator(generator.GenerationOptions{Seed: 1}) + + t.Run("ZeroCount", func(t *testing.T) { + input := generator.HistogramInput{Count: 0} + result, err := gen.GenerateHistogram(input, nil) + + if err != nil { + t.Fatalf("Expected no error with zero count, got %v", err) + } + + // Should default to some reasonable count + if result.Expected.Count == 0 { + t.Error("Expected non-zero count when input count is 0") + } + }) + + t.Run("NilValueFunc", func(t *testing.T) { + input := generator.HistogramInput{Count: 10} + result, err := gen.GenerateHistogram(input, nil) + + if err != nil { + t.Fatalf("Expected no error with nil value func, got %v", err) + } + + if result.Expected.Count == 0 { + t.Error("Expected samples even with nil value function") + } + }) + + t.Run("EmptyBoundaries", func(t *testing.T) { + input := generator.HistogramInput{Count: 10, Boundaries: []float64{}} + result, err := gen.GenerateHistogram(input, nil) + + if err != nil { + t.Fatalf("Expected no error with empty boundaries, got %v", err) + } + + // Should generate default boundaries + if len(result.Input.Boundaries) == 0 { + t.Error("Expected default boundaries when none provided") + } + }) +} + +// Helper function for absolute value +func abs(x float64) float64 { + if x < 0 { + return -x + } + return x +} + +// Helper function to create float64 pointers +func ptr(f float64) *float64 { + return &f +} diff --git a/cmd/generator/generator.go b/cmd/generator/generator.go new file mode 100644 index 0000000000000..86c6f5bd42480 --- /dev/null +++ b/cmd/generator/generator.go @@ -0,0 +1,33 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package generator provides histogram generation capabilities for OpenTelemetry metrics testing. +// This package is designed to generate realistic histogram data using various statistical distributions +// and can optionally publish the generated metrics to OTLP endpoints. +// +// The package is organized into several focused modules: +// - types.go: Core data structures and types +// - histogram_generator.go: Main histogram generation logic +// - distributions.go: Statistical distribution functions +// - otlp_publisher.go: OTLP endpoint publishing functionality +// +// Example usage: +// +// generator := NewHistogramGenerator(GenerationOptions{ +// Seed: 12345, +// Endpoint: "localhost:4318", +// }) +// +// result, err := generator.GenerateAndPublishHistograms( +// HistogramInput{ +// Count: 1000, +// Min: ptr(10.0), +// Max: ptr(200.0), +// Boundaries: []float64{25, 50, 75, 100, 150}, +// Attributes: map[string]string{"service.name": "test-service"}, +// }, +// func(rnd *rand.Rand, t time.Time) float64 { +// return NormalRandom(rnd, 50, 15) +// }, +// ) +package generator diff --git a/cmd/generator/go.sum b/cmd/generator/go.sum index 322e7e92bef78..d071adc237d1e 100644 --- a/cmd/generator/go.sum +++ b/cmd/generator/go.sum @@ -1,13 +1,19 @@ +<<<<<<< HEAD github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +======= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +>>>>>>> 18b7de606a (Add OpenTelemetry Histogram Generator (#366)) github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +<<<<<<< HEAD github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -75,10 +81,39 @@ go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0 h1:i8YpvWGm/Uq go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0/go.mod h1:pQ70xHY/ZVxNUBPn+qUWPl8nwai87eWdqL3M37lNi9A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +======= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0 h1:/PSf7CIVu//VV7zYeYhnOLIgMsrONH37XV2mzeVtjZk= +github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen v0.135.0/go.mod h1:RpbRtcf6cXpgn8aJOf6SvIuwGo2ycRUEI2HASamCjlw= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 h1:zG8GlgXCJQd5BU98C0hZnBbElszTmUgCNCfYneaDL0A= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0/go.mod h1:hOfBCz8kv/wuq73Mx2H2QnWokh/kHZxkh6SNF2bdKtw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 h1:9PgnL3QNlj10uGxExowIDIZu66aVBwWhXmbOp1pa6RA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0/go.mod h1:0ineDcLELf6JmKfuo0wvvhAVMuxWFYvkTin2iV4ydPQ= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= +>>>>>>> 18b7de606a (Add OpenTelemetry Histogram Generator (#366)) go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +<<<<<<< HEAD golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -124,3 +159,21 @@ google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aO google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +======= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0 h1:0UOBWO4dC+e51ui0NFKSPbkHHiQ4TmrEfEZMLDyRmY8= +google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0/go.mod h1:8ytArBbtOy2xfht+y2fqKd5DRDJRUQhqbyEnQ4bDChs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 h1:MAKi5q709QWfnkkpNQ0M12hYJ1+e8qYVDyowc4U1XZM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +>>>>>>> 18b7de606a (Add OpenTelemetry Histogram Generator (#366)) diff --git a/cmd/generator/histogram_generator.go b/cmd/generator/histogram_generator.go new file mode 100644 index 0000000000000..0912a7abbf95a --- /dev/null +++ b/cmd/generator/histogram_generator.go @@ -0,0 +1,222 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "math" + "math/rand" + "sort" + "time" +) + +// HistogramGenerator generates histogram test cases using statistical distributions +type HistogramGenerator struct { + rand *rand.Rand + endpoint string +} + +// NewHistogramGenerator creates a new histogram generator with deterministic seed +func NewHistogramGenerator(opt ...GenerationOptions) *HistogramGenerator { + var seed int64 = time.Now().UnixNano() + var endpoint string + + if len(opt) > 0 { + if opt[0].Seed != 0 { + seed = opt[0].Seed + } + endpoint = opt[0].Endpoint + } + + return &HistogramGenerator{ + rand: rand.New(rand.NewSource(seed)), + endpoint: endpoint, + } +} + +// GenerateHistogram generates histogram data from individual values using a value function +func (g *HistogramGenerator) GenerateHistogram(input HistogramInput, valueFunc func(*rand.Rand, time.Time) float64) (HistogramResult, error) { + timestamp := time.Now() + sampleCount := int(input.Count) + + if sampleCount <= 0 { + sampleCount = 1000 // default sample count + } + + // Generate individual values using the value function + values := make([]float64, sampleCount) + for i := 0; i < sampleCount; i++ { + if valueFunc != nil { + values[i] = valueFunc(g.rand, timestamp) + } else { + values[i] = g.rand.Float64() * 100 // default random value + } + } + + // Sort values to find min/max + sort.Float64s(values) + + // Calculate basic stats + var sum float64 + for _, v := range values { + sum += v + } + + generatedMin := values[0] + generatedMax := values[len(values)-1] + average := sum / float64(len(values)) + + // Determine final min/max values + var finalMin, finalMax float64 + if input.Min != nil { + finalMin = *input.Min + } else { + finalMin = generatedMin + } + if input.Max != nil { + finalMax = *input.Max + } else { + finalMax = generatedMax + } + + // Use provided boundaries or generate them based on min/max + boundaries := input.Boundaries + if len(boundaries) == 0 { + boundaries = generateBoundariesBetween(finalMin, finalMax, 10) + } + + counts := make([]uint64, len(boundaries)+1) + for _, value := range values { + bucketIndex := len(boundaries) // default to overflow bucket + for i, boundary := range boundaries { + if value <= boundary { + bucketIndex = i + break + } + } + counts[bucketIndex]++ + } + + // Calculate percentile ranges + percentileRanges := g.calculatePercentileRangesFromValues(values, boundaries) + + // Use input min/max if provided, otherwise use generated values + var resultMin, resultMax *float64 + if input.Min != nil { + resultMin = input.Min + } else { + resultMin = &generatedMin + } + if input.Max != nil { + resultMax = input.Max + } else { + resultMax = &generatedMax + } + + generatedInput := HistogramInput{ + Count: uint64(len(values)), + Sum: sum, + Min: resultMin, + Max: resultMax, + Boundaries: boundaries, + Counts: counts, + Attributes: input.Attributes, + } + + expected := ExpectedMetrics{ + Count: uint64(len(values)), + Sum: sum, + Average: average, + Min: resultMin, + Max: resultMax, + PercentileRanges: percentileRanges, + } + + return HistogramResult{ + Input: generatedInput, + Expected: expected, + }, nil +} + +// GenerateAndPublishHistograms generates and optionally publishes histogram data +func (g *HistogramGenerator) GenerateAndPublishHistograms(input HistogramInput, valueFunc func(*rand.Rand, time.Time) float64) (HistogramResult, error) { + res, err := g.GenerateHistogram(input, valueFunc) + if err != nil { + return HistogramResult{}, err + } + + if g.endpoint == "" { + return res, nil + } + + publisher := NewOTLPPublisher(g.endpoint) + err = publisher.SendHistogramMetric("TelemetryGen", res) + if err != nil { + return HistogramResult{}, err + } + + return res, nil +} + +// calculatePercentileRangesFromValues calculates percentile ranges for sorted values +func (g *HistogramGenerator) calculatePercentileRangesFromValues(sortedValues []float64, boundaries []float64) map[float64]PercentileRange { + percentiles := []float64{0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99} + ranges := make(map[float64]PercentileRange) + + for _, p := range percentiles { + index := int(p * float64(len(sortedValues))) + if index >= len(sortedValues) { + index = len(sortedValues) - 1 + } + + value := sortedValues[index] + + // Find which bucket this percentile value falls into + var low, high float64 + + // Check if value falls in any boundary bucket + bucketFound := false + for i, boundary := range boundaries { + if value <= boundary { + if i > 0 { + low = boundaries[i-1] + } else { + low = math.Inf(-1) + } + high = boundary + bucketFound = true + break + } + } + + // If not found in any boundary bucket, it's in the overflow bucket + if !bucketFound { + if len(boundaries) > 0 { + low = boundaries[len(boundaries)-1] + } else { + low = math.Inf(-1) + } + high = math.Inf(1) + } + + ranges[p] = PercentileRange{Low: low, High: high} + } + + return ranges +} + +// generateBoundariesBetween creates evenly spaced boundaries between min and max +func generateBoundariesBetween(min, max float64, numBuckets int) []float64 { + if numBuckets <= 0 { + numBuckets = 10 + } + + boundaries := make([]float64, numBuckets-1) + step := (max - min) / float64(numBuckets) + + for i := 0; i < numBuckets-1; i++ { + boundaries[i] = min + float64(i+1)*step + } + + return boundaries +} diff --git a/cmd/generator/otlp_publisher.go b/cmd/generator/otlp_publisher.go new file mode 100644 index 0000000000000..231fded29ef7a --- /dev/null +++ b/cmd/generator/otlp_publisher.go @@ -0,0 +1,74 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "fmt" + + "github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/pkg/metrics" +) + +// OTLPPublisher handles publishing metrics to OTLP endpoints using telemetrygen +type OTLPPublisher struct { + endpoint string +} + +// NewOTLPPublisher creates a new OTLP publisher using telemetrygen +func NewOTLPPublisher(endpoint string) *OTLPPublisher { + return &OTLPPublisher{ + endpoint: endpoint, + } +} + +// SendHistogramMetric sends a histogram metric using telemetrygen +func (p *OTLPPublisher) SendHistogramMetric(metricName string, result HistogramResult) error { + return p.SendMetric(metricName, "Histogram", 0) // Histogram value doesn't matter for telemetrygen +} + +// SendMetric sends a metric using telemetrygen with the specified type and value +func (p *OTLPPublisher) SendMetric(metricName string, metricType string, value float64) error { + // Create telemetrygen config + cfg := metrics.NewConfig() + cfg.CustomEndpoint = p.endpoint + cfg.UseHTTP = true + cfg.Insecure = true + cfg.NumMetrics = 1 + cfg.Rate = 1 + cfg.MetricName = metricName + + // Set metric type + switch metricType { + case "Sum": + cfg.MetricType = metrics.MetricTypeSum + case "Gauge": + cfg.MetricType = metrics.MetricTypeGauge + case "Histogram": + cfg.MetricType = metrics.MetricTypeHistogram + default: + cfg.MetricType = metrics.MetricTypeGauge // default to gauge + } + + // Start the metrics generation + err := metrics.Start(cfg) + if err != nil { + return fmt.Errorf("failed to send metric via telemetrygen: %v", err) + } + + return nil +} + +// SendGaugeMetric sends a gauge metric +func (p *OTLPPublisher) SendGaugeMetric(metricName string, value float64) error { + return p.SendMetric(metricName, "Gauge", value) +} + +// SendSumMetric sends a sum/counter metric +func (p *OTLPPublisher) SendSumMetric(metricName string, value float64) error { + return p.SendMetric(metricName, "Sum", value) +} + +// SendHistogramMetricSimple sends a histogram metric (telemetrygen will generate histogram data) +func (p *OTLPPublisher) SendHistogramMetricSimple(metricName string) error { + return p.SendMetric(metricName, "Histogram", 0) +} diff --git a/cmd/generator/types.go b/cmd/generator/types.go new file mode 100644 index 0000000000000..6660fd684d52e --- /dev/null +++ b/cmd/generator/types.go @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package generator + +// PercentileRange represents a range for percentile calculations +type PercentileRange struct { + Low float64 + High float64 +} + +// HistogramInput represents the input data for a histogram metric +type HistogramInput struct { + Count uint64 + Sum float64 + Min *float64 + Max *float64 + Boundaries []float64 + Counts []uint64 + Attributes map[string]string +} + +// ExpectedMetrics represents the expected calculated metrics from histogram data +type ExpectedMetrics struct { + Count uint64 + Sum float64 + Average float64 + Min *float64 + Max *float64 + PercentileRanges map[float64]PercentileRange +} + +// HistogramResult combines input and expected metrics +type HistogramResult struct { + Input HistogramInput + Expected ExpectedMetrics +} + +// GenerationOptions configures histogram generation +type GenerationOptions struct { + Seed int64 + Endpoint string +} From 4dff397492f8ce10c3f5aa688fdac4f18459efd0 Mon Sep 17 00:00:00 2001 From: Rick Rossi Date: Thu, 16 Oct 2025 16:36:09 -0400 Subject: [PATCH 23/23] Add sum to histogram visualizations --- pkg/aws/cloudwatch/histograms/conversion_test.go | 7 ++++--- .../histograms/testdata/histogram_mappings.py | 13 +++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pkg/aws/cloudwatch/histograms/conversion_test.go b/pkg/aws/cloudwatch/histograms/conversion_test.go index a8d980c78c6fd..a2fa871c0fa83 100644 --- a/pkg/aws/cloudwatch/histograms/conversion_test.go +++ b/pkg/aws/cloudwatch/histograms/conversion_test.go @@ -45,8 +45,8 @@ func TestConvertOTelToCloudWatch(t *testing.T) { // uncomment next lines to write datapoint to JSON file for visual inspection // use histogram_mappings.py to create graphs - //os.Mkdir("testdata/exponential", os.ModePerm) - //assert.NoError(t, writeValuesAndCountsToJson(dist, "testdata/exponential/"+filenameReplacer.Replace(tc.Name+".json"))) + os.Mkdir("testdata/exponential", os.ModePerm) + assert.NoError(t, writeValuesAndCountsToJson(dist, "testdata/exponential/"+filenameReplacer.Replace(tc.Name+".json"))) }) } @@ -289,9 +289,10 @@ func verifyDistAccuracy(t *testing.T, newDistFunc func(pmetric.HistogramDataPoin func writeValuesAndCountsToJson(dist cloudwatch.HistogramDataPoint, filename string) error { values, counts := dist.ValuesAndCounts() - data := make(map[string][]float64) + data := make(map[string]any) data["values"] = values data["counts"] = counts + data["sum"] = dist.Sum() jsonData, err := json.MarshalIndent(data, "", " ") if err != nil { diff --git a/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py b/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py index ea45cd8a86751..6fa92abf6be25 100644 --- a/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py +++ b/pkg/aws/cloudwatch/histograms/testdata/histogram_mappings.py @@ -15,6 +15,7 @@ def plot_input_histogram(data, ax, title: str, color: str): counts = data['Counts'] min_val = data.get('Min') max_val = data.get('Max') + summ = data.get('Sum') total_count = sum(counts) # Handle case with no boundaries (single bucket) @@ -49,11 +50,11 @@ def plot_input_histogram(data, ax, title: str, color: str): widths.append(right - left) ax.bar(left_edges, counts, width=widths, alpha=0.7, edgecolor='black', linewidth=0.8, color=color, align='edge') - ax.set_title(f'{title} (Count: {total_count})') + ax.set_title(f'{title} (Count: {total_count}, Sum: {summ})') ax.set_ylabel('Counts') ax.grid(True, alpha=0.3) -def plot_cw_histogram_bars(histogram: Dict[float, float], histogram_min: float, histogram_max: float, ax, title: str, color: str): +def plot_cw_histogram_bars(histogram: Dict[float, float], histogram_min: float, histogram_max: float, histogram_sum: float, ax, title: str, color: str): """Plot histogram bars on given axes.""" values = sorted(histogram.keys()) counts = [histogram[v] for v in values] @@ -92,7 +93,7 @@ def plot_cw_histogram_bars(histogram: Dict[float, float], histogram_min: float, ax.bar(values, counts, width=widths, alpha=0.7, edgecolor='black', linewidth=0.8, color=color) ax.scatter(values, counts, color='red', s=50, zorder=5) - ax.set_title(f'{title} (Count: {total_count})') + ax.set_title(f'{title} (Count: {total_count}, Sum: {histogram_sum})') ax.set_ylabel('Counts') ax.grid(True, alpha=0.3) @@ -100,7 +101,7 @@ def load_json_data(filepath): """Load histogram data from JSON file.""" with open(filepath, 'r') as f: data = json.load(f) - return data['values'], data['counts'] + return data['values'], data['counts'], data['sum'] def load_input_histogram(filepath): """Load input histogram format.""" @@ -126,11 +127,11 @@ def plot_all_folders_comparison(json_filename): data = load_input_histogram(filepath) plot_input_histogram(data, ax[i], f'{folder.capitalize()} Mapping', color) else: - values, counts = load_json_data(filepath) + values, counts, summ = load_json_data(filepath) if not values: # Skip if no values continue hist = {values[j]: counts[j] for j in range(len(values))} - plot_cw_histogram_bars(hist, min(values), max(values), ax[i], f'{folder.capitalize()} Mapping', color) + plot_cw_histogram_bars(hist, min(values), max(values), summ, ax[i], f'{folder.capitalize()} Mapping', color) except Exception as e: print(f"Error processing {filepath}: {e}")