diff options
| author | Franck Cuny <franck@fcuny.net> | 2025-08-25 07:42:40 -0700 |
|---|---|---|
| committer | Franck Cuny <franck@fcuny.net> | 2025-08-25 07:42:40 -0700 |
| commit | 468e0257e9be401aebde8b94ddd753c17700ba80 (patch) | |
| tree | 17a48e7e0037e5ee33787c38406a0d0e6b2da34f /cmd/seq-stat/main.go | |
| parent | add nix configurations for building and running `goget` (diff) | |
| download | x-468e0257e9be401aebde8b94ddd753c17700ba80.tar.gz | |
add seq-stat: terminal histogram generator for number sequences
Generates ASCII histograms using Unicode block characters for sequences
of numbers provided via command line arguments or STDIN input.
Diffstat (limited to 'cmd/seq-stat/main.go')
| -rw-r--r-- | cmd/seq-stat/main.go | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/cmd/seq-stat/main.go b/cmd/seq-stat/main.go new file mode 100644 index 0000000..75bb0a3 --- /dev/null +++ b/cmd/seq-stat/main.go @@ -0,0 +1,115 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +var ticks = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'} + +func histogram(sequence []float64) []rune { + if len(sequence) == 0 { + return []rune{} + } + + minVal := sequence[0] + maxVal := sequence[0] + + for _, v := range sequence { + if v < minVal { + minVal = v + } + if v > maxVal { + maxVal = v + } + } + + scale := (maxVal - minVal) * 256 / float64(len(ticks)-1) + if scale < 1 { + scale = 1 + } + + result := make([]rune, len(sequence)) + for i, v := range sequence { + index := int(((v - minVal) * 256) / scale) + if index >= len(ticks) { + index = len(ticks) - 1 + } + result[i] = ticks[index] + } + + return result +} + +func parseNumbers(input string) ([]float64, error) { + fields := strings.Fields(input) + numbers := make([]float64, 0, len(fields)) + + for _, field := range fields { + num, err := strconv.ParseFloat(field, 64) + if err != nil { + return nil, fmt.Errorf("invalid number: %s", field) + } + numbers = append(numbers, num) + } + + return numbers, nil +} + +func readFromStdin() ([]float64, error) { + var numbers []float64 + scanner := bufio.NewScanner(os.Stdin) + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" { + continue + } + + lineNumbers, err := parseNumbers(line) + if err != nil { + return nil, err + } + numbers = append(numbers, lineNumbers...) + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading from stdin: %v", err) + } + + return numbers, nil +} + +func main() { + var numbers []float64 + var err error + + if len(os.Args) > 1 { + numbers = make([]float64, 0, len(os.Args)-1) + for _, arg := range os.Args[1:] { + num, parseErr := strconv.ParseFloat(arg, 64) + if parseErr != nil { + fmt.Fprintf(os.Stderr, "Invalid number: %s\n", arg) + os.Exit(1) + } + numbers = append(numbers, num) + } + } else { + numbers, err = readFromStdin() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + if len(numbers) == 0 { + fmt.Fprintln(os.Stderr, "No numbers provided") + os.Exit(1) + } + } + + h := histogram(numbers) + fmt.Println(string(h)) +} |
