From 1b12d6ca8914aff61605311d2bac00a136921f48 Mon Sep 17 00:00:00 2001 From: Andreas Solleder Date: Sat, 6 Jun 2026 00:20:16 +0200 Subject: [PATCH 1/2] =?UTF-8?q?feat(metrics):=20R=C3=A9nyi=20entropy=20spe?= =?UTF-8?q?ctrum=20&=20Hill=20numbers=20(#46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add CodeQA.Metrics.File.RenyiEntropy: generalizes Shannon entropy across order parameter α ∈ {0,1,2,∞} over the token distribution, yielding a spectrum H₀ ≥ H₁ ≥ H₂ ≥ H_∞ that characterizes distribution shape rather than a single scalar. - renyi_0 (Hartley), renyi_1 (Shannon limit), renyi_2 (collision), renyi_inf (min-entropy) - hill_1, hill_2 = 2^H_α — effective vocabulary size - spectrum_slope = H₀ − H₂ — concentration indicator - edge cases: empty → zeros; single token → H=0, Hill=1.0 - registered after LexicalConcentration (complementary concentration measure) --- lib/codeqa/engine/analyzer.ex | 1 + lib/codeqa/metrics/file/renyi_entropy.ex | 103 ++++++++++++++++ .../metrics/file/renyi_entropy_test.exs | 113 ++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 lib/codeqa/metrics/file/renyi_entropy.ex create mode 100644 test/codeqa/metrics/file/renyi_entropy_test.exs diff --git a/lib/codeqa/engine/analyzer.ex b/lib/codeqa/engine/analyzer.ex index da35826..18bab5e 100644 --- a/lib/codeqa/engine/analyzer.ex +++ b/lib/codeqa/engine/analyzer.ex @@ -17,6 +17,7 @@ defmodule CodeQA.Engine.Analyzer do |> Registry.register_file_metric(Metrics.Heaps) |> Registry.register_file_metric(Metrics.Vocabulary) |> Registry.register_file_metric(Metrics.LexicalConcentration) + |> Registry.register_file_metric(Metrics.RenyiEntropy) |> Registry.register_file_metric(Metrics.Ngram) |> Registry.register_file_metric(Metrics.Halstead) |> Registry.register_file_metric(Metrics.Readability) diff --git a/lib/codeqa/metrics/file/renyi_entropy.ex b/lib/codeqa/metrics/file/renyi_entropy.ex new file mode 100644 index 0000000..0c31be9 --- /dev/null +++ b/lib/codeqa/metrics/file/renyi_entropy.ex @@ -0,0 +1,103 @@ +defmodule CodeQA.Metrics.File.RenyiEntropy do + @moduledoc """ + Computes the Rényi entropy spectrum and Hill numbers over the token + distribution. + + Rényi entropy `H_α` generalizes Shannon entropy across an order parameter + `α`: low orders weight rare tokens, high orders only the dominant ones. The + spectrum `H₀ ≥ H₁ ≥ H₂ ≥ H_∞` therefore characterizes the *shape* of the + distribution rather than a single scalar. `spectrum_slope = H₀ − H₂` is a + concentration indicator: near zero for a flat (uniform) vocabulary, large + when a few tokens dominate. + + Hill numbers `D_α = 2^(H_α)` express the *effective vocabulary size* — how + many tokens really count — which is more interpretable than raw `vocab_size`. + + See [Rényi entropy](https://en.wikipedia.org/wiki/R%C3%A9nyi_entropy) and + [Hill numbers / diversity](https://en.wikipedia.org/wiki/Diversity_index#Effective_number_of_species). + """ + + @behaviour CodeQA.Metrics.File.FileMetric + + @impl true + def name, do: "renyi_entropy" + + @impl true + def keys, + do: [ + "renyi_0", + "renyi_1", + "renyi_2", + "renyi_inf", + "hill_1", + "hill_2", + "spectrum_slope" + ] + + @impl true + def description, + do: + "Rényi entropy spectrum (α ∈ {0,1,2,∞}) and Hill numbers (effective vocabulary size) over the token distribution." + + @spec analyze(map()) :: map() + @impl true + def analyze(%{tokens: []}), do: zero_result() + + def analyze(%{token_counts: token_counts, tokens: tokens}) do + total = length(tokens) + probs = token_counts |> Map.values() |> Enum.map(&(&1 / total)) + spectrum(probs) + end + + defp spectrum(probs) do + h0 = renyi_0(probs) + h1 = renyi_1(probs) + h2 = renyi_2(probs) + h_inf = renyi_inf(probs) + + %{ + "renyi_0" => round4(h0), + "renyi_1" => round4(h1), + "renyi_2" => round4(h2), + "renyi_inf" => round4(h_inf), + "hill_1" => round4(hill(h1)), + "hill_2" => round4(hill(h2)), + "spectrum_slope" => round4(h0 - h2) + } + end + + # H₀ = log2(support size) — Hartley entropy, counts only distinct tokens. + defp renyi_0(probs), do: log2(length(probs)) + + # H₁ = -Σ pᵢ log2 pᵢ — Shannon entropy, the α→1 limit. + defp renyi_1(probs), + do: -Enum.reduce(probs, 0.0, fn p, acc -> acc + p * :math.log2(p) end) + + # H₂ = -log2(Σ pᵢ²) — collision entropy. + defp renyi_2(probs) do + sum_sq = Enum.reduce(probs, 0.0, fn p, acc -> acc + p * p end) + -:math.log2(sum_sq) + end + + # H_∞ = -log2(max pᵢ) — min-entropy, governed by the most frequent token. + defp renyi_inf(probs), do: -:math.log2(Enum.max(probs)) + + # Hill number D_α = 2^(H_α), the effective vocabulary size. + defp hill(h), do: :math.pow(2, h) + + defp log2(n) when n > 0, do: :math.log2(n) + defp log2(_), do: 0.0 + + defp round4(x), do: Float.round(x * 1.0, 4) + + defp zero_result, + do: %{ + "renyi_0" => 0.0, + "renyi_1" => 0.0, + "renyi_2" => 0.0, + "renyi_inf" => 0.0, + "hill_1" => 0.0, + "hill_2" => 0.0, + "spectrum_slope" => 0.0 + } +end diff --git a/test/codeqa/metrics/file/renyi_entropy_test.exs b/test/codeqa/metrics/file/renyi_entropy_test.exs new file mode 100644 index 0000000..110acc3 --- /dev/null +++ b/test/codeqa/metrics/file/renyi_entropy_test.exs @@ -0,0 +1,113 @@ +defmodule CodeQA.Metrics.File.RenyiEntropyTest do + use ExUnit.Case, async: true + + alias CodeQA.Engine.Pipeline + alias CodeQA.Metrics.File.RenyiEntropy + + defp ctx(code), do: Pipeline.build_file_context(code) + defp result(code), do: RenyiEntropy.analyze(ctx(code)) + + describe "analyze/1 - edge cases" do + test "returns zeros for empty content" do + assert result("") == %{ + "renyi_0" => 0.0, + "renyi_1" => 0.0, + "renyi_2" => 0.0, + "renyi_inf" => 0.0, + "hill_1" => 0.0, + "hill_2" => 0.0, + "spectrum_slope" => 0.0 + } + end + + test "single unique token has zero entropy across the spectrum" do + r = result("x x x x") + assert r["renyi_0"] == 0.0 + assert r["renyi_1"] == 0.0 + assert r["renyi_2"] == 0.0 + assert r["renyi_inf"] == 0.0 + assert r["spectrum_slope"] == 0.0 + end + + test "single unique token has hill numbers equal to vocab_size of 1" do + r = result("x x x x") + assert r["hill_1"] == 1.0 + assert r["hill_2"] == 1.0 + end + end + + describe "analyze/1 - uniform distribution (flat spectrum)" do + test "uniform tokens give renyi_0 == renyi_2 and zero slope" do + r = result("a b c d e f g h") + assert r["renyi_0"] == r["renyi_2"] + assert r["spectrum_slope"] == 0.0 + end + + test "uniform tokens give renyi_0 == renyi_inf (all orders collapse)" do + r = result("a b c d e f g h") + assert r["renyi_0"] == r["renyi_inf"] + assert r["renyi_1"] == r["renyi_0"] + end + + test "uniform vocab of 8 yields renyi_0 of log2(8) == 3.0" do + r = result("a b c d e f g h") + assert r["renyi_0"] == 3.0 + end + + test "hill numbers equal vocab_size for a uniform distribution" do + r = result("a b c d e f g h") + assert r["hill_1"] == 8.0 + assert r["hill_2"] == 8.0 + end + end + + describe "analyze/1 - dominated distribution (steep spectrum)" do + test "dominated token gives large positive spectrum_slope" do + # x: 100× against 8 singletons → H₀ = log2(9) ≈ 3.17 but H₂ collapses + # toward 0, so the slope is large. (With only 2 distinct tokens H₀ caps + # at 1.0, which bounds the slope below 1 — needs a wider vocab to show.) + code = String.duplicate("x ", 100) <> "a b c d e f g h" + assert result(code)["spectrum_slope"] > 1.0 + end + + test "renyi monotonically decreases with order for a skewed distribution" do + code = "x x x x x x x y" + r = result(code) + assert r["renyi_0"] >= r["renyi_1"] + assert r["renyi_1"] >= r["renyi_2"] + assert r["renyi_2"] >= r["renyi_inf"] + end + + test "hill_2 is much smaller than vocab_size when one token dominates" do + # vocab_size of 5 but effective vocabulary collapses toward 1 + code = String.duplicate("x ", 100) <> "a b c d" + vocab_size = 5 + assert result(code)["hill_2"] < vocab_size / 2 + end + end + + describe "behaviour contract" do + test "name/0 is renyi_entropy" do + assert RenyiEntropy.name() == "renyi_entropy" + end + + test "keys/0 matches the keys returned by analyze/1" do + keys = RenyiEntropy.keys() + r = result("a b c a b") + assert Enum.sort(keys) == Enum.sort(Map.keys(r)) + end + + test "keys/0 lists exactly the seven spec keys" do + assert Enum.sort(RenyiEntropy.keys()) == + Enum.sort([ + "renyi_0", + "renyi_1", + "renyi_2", + "renyi_inf", + "hill_1", + "hill_2", + "spectrum_slope" + ]) + end + end +end From 285fffe5f245d8270a047b7e7e0060c7f1713087 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 5 Jun 2026 22:23:33 +0000 Subject: [PATCH 2/2] chore(combined-metrics): sync language coverage and scalar vectors [skip ci] --- priv/combined_metrics/code_smells.yml | 50 +++++++- priv/combined_metrics/consistency.yml | 37 +++++- priv/combined_metrics/dependencies.yml | 26 +++- priv/combined_metrics/documentation.yml | 54 +++++++- priv/combined_metrics/error_handling.yml | 30 ++++- priv/combined_metrics/file_structure.yml | 49 ++++++- priv/combined_metrics/function_design.yml | 74 +++++++++-- priv/combined_metrics/naming_conventions.yml | 28 +++- .../combined_metrics/scope_and_assignment.yml | 58 ++++++++- priv/combined_metrics/testing.yml | 40 +++++- priv/combined_metrics/type_and_value.yml | 49 ++++++- priv/combined_metrics/variable_naming.yml | 121 ++++++++++++++++-- 12 files changed, 550 insertions(+), 66 deletions(-) diff --git a/priv/combined_metrics/code_smells.yml b/priv/combined_metrics/code_smells.yml index aee9623..2b16b98 100644 --- a/priv/combined_metrics/code_smells.yml +++ b/priv/combined_metrics/code_smells.yml @@ -1,7 +1,7 @@ consistent_string_quote_style: _doc: "Files should use a single, consistent string quoting style throughout." _languages: [elixir] - _log_baseline: -19.2276 + _log_baseline: -20.1399 branching: mean_branching_density: 0.0243 mean_non_blank_count: -0.0248 @@ -89,6 +89,14 @@ consistent_string_quote_style: mean_flesch_adapted: 0.0046 mean_fog_adapted: -0.0301 mean_total_lines: -0.0248 + renyi_entropy: + mean_hill_1: -0.0705 + mean_hill_2: -0.1119 + mean_renyi_0: -0.0141 + mean_renyi_1: -0.0178 + mean_renyi_2: -0.0322 + mean_renyi_inf: -0.1217 + mean_spectrum_slope: 0.0390 separator_counts: mean_dot_count: -0.1743 mean_underscore_count: -0.0644 @@ -110,7 +118,7 @@ consistent_string_quote_style: no_dead_code_after_return: _doc: "There should be no unreachable statements after a return or early exit." _excludes_languages: [elixir] - _log_baseline: -70.7283 + _log_baseline: -71.9927 branching: mean_branch_count: -0.9417 mean_branching_density: -0.1794 @@ -209,6 +217,14 @@ no_dead_code_after_return: mean_flesch_adapted: 0.0346 mean_fog_adapted: 0.0141 mean_total_lines: -0.7334 + renyi_entropy: + mean_hill_1: -0.1341 + mean_hill_2: -0.1381 + mean_renyi_0: -0.0501 + mean_renyi_1: -0.0360 + mean_renyi_2: -0.0409 + mean_renyi_inf: -0.0720 + mean_spectrum_slope: -0.0737 separator_counts: mean_dot_count: -1.2574 mean_hyphen_count: -1.8488 @@ -233,7 +249,7 @@ no_dead_code_after_return: no_debug_print_statements: _doc: "Debug output (`console.log`, `IO.inspect`, `fmt.Println`) must not be left in committed code." _languages: [elixir, go, javascript, python, ruby] - _log_baseline: -29.5797 + _log_baseline: -29.0583 branching: mean_branch_count: 0.1865 mean_branching_density: 0.4692 @@ -338,6 +354,14 @@ no_debug_print_statements: mean_flesch_adapted: -0.0375 mean_fog_adapted: 0.0931 mean_total_lines: -0.1329 + renyi_entropy: + mean_hill_1: 0.0824 + mean_hill_2: 0.0412 + mean_renyi_0: 0.0188 + mean_renyi_1: 0.0224 + mean_renyi_2: 0.0114 + mean_renyi_inf: -0.0190 + mean_spectrum_slope: 0.0415 separator_counts: mean_dot_count: -0.1479 mean_hyphen_count: 0.0827 @@ -362,7 +386,7 @@ no_debug_print_statements: no_fixme_comments: _doc: "FIXME, XXX, and HACK comments indicate known problems that should be resolved before merging." _languages: [elixir, go, javascript, python, ruby] - _log_baseline: 12.0359 + _log_baseline: 10.9439 branching: mean_branch_count: 0.1713 mean_branching_density: 0.1042 @@ -471,6 +495,14 @@ no_fixme_comments: mean_flesch_adapted: -0.0152 mean_fog_adapted: -0.0037 mean_total_lines: 0.0927 + renyi_entropy: + mean_hill_1: -0.1284 + mean_hill_2: -0.1032 + mean_renyi_0: -0.0222 + mean_renyi_1: -0.0313 + mean_renyi_2: -0.0296 + mean_renyi_inf: -0.0276 + mean_spectrum_slope: -0.0024 separator_counts: mean_dot_count: 0.3139 mean_hyphen_count: -0.0251 @@ -494,7 +526,7 @@ no_fixme_comments: no_nested_ternary: _doc: "Nested conditional expressions (ternary-within-ternary) are harder to read than a plain if-else." _excludes_languages: [elixir] - _log_baseline: 8.9473 + _log_baseline: 8.7697 branching: mean_branch_count: -0.5662 mean_branching_density: -0.3441 @@ -594,6 +626,14 @@ no_nested_ternary: mean_flesch_adapted: -0.0367 mean_fog_adapted: 0.3545 mean_total_lines: -0.2221 + renyi_entropy: + mean_hill_1: -0.0050 + mean_hill_2: -0.0440 + mean_renyi_0: 0.0110 + mean_renyi_1: -0.0014 + mean_renyi_2: -0.0142 + mean_renyi_inf: -0.0561 + mean_spectrum_slope: 0.0714 separator_counts: mean_dot_count: -0.1824 mean_hyphen_count: -0.1067 diff --git a/priv/combined_metrics/consistency.yml b/priv/combined_metrics/consistency.yml index 68b3875..107c44e 100644 --- a/priv/combined_metrics/consistency.yml +++ b/priv/combined_metrics/consistency.yml @@ -1,7 +1,7 @@ consistent_casing_within_file: _doc: "A file should use one naming convention throughout — no mixing of camelCase and snake_case for the same kind of identifier." _languages: [elixir] - _log_baseline: -0.6528 + _log_baseline: -0.8912 brevity: mean_sample_size: -0.0471 casing_entropy: @@ -48,6 +48,13 @@ consistent_casing_within_file: mean_trigram_unique: -0.0172 readability: mean_avg_line_length: 0.0221 + renyi_entropy: + mean_hill_1: -0.0362 + mean_hill_2: -0.0104 + mean_renyi_0: -0.0101 + mean_renyi_1: -0.0090 + mean_renyi_2: -0.0030 + mean_spectrum_slope: -0.0302 separator_counts: mean_underscore_count: 0.4311 symbol_density: @@ -65,7 +72,7 @@ consistent_casing_within_file: consistent_error_return_shape: _doc: "All functions in a module should return errors in the same shape — mixed `nil`, `false`, and `{:error, _}` returns are confusing." _languages: [elixir] - _log_baseline: -2.8073 + _log_baseline: -3.3807 branching: mean_branch_count: -0.2800 mean_branching_density: 0.5563 @@ -163,6 +170,13 @@ consistent_error_return_shape: mean_flesch_adapted: -0.0433 mean_fog_adapted: 0.1454 mean_total_lines: -0.3834 + renyi_entropy: + mean_hill_1: -0.0658 + mean_hill_2: -0.0443 + mean_renyi_0: -0.0167 + mean_renyi_1: -0.0195 + mean_renyi_2: -0.0216 + mean_renyi_inf: -0.0655 separator_counts: mean_dot_count: 0.6069 mean_hyphen_count: -1.1258 @@ -183,7 +197,7 @@ consistent_error_return_shape: consistent_function_style: _doc: "A module should not mix one-liner and multi-clause function definitions for the same concern." _languages: [elixir] - _log_baseline: 57.4562 + _log_baseline: 60.0235 branching: mean_branch_count: 0.3619 mean_branching_density: 0.2247 @@ -293,6 +307,14 @@ consistent_function_style: mean_flesch_adapted: 0.0263 mean_fog_adapted: -0.1088 mean_total_lines: 0.3305 + renyi_entropy: + mean_hill_1: 0.3154 + mean_hill_2: 0.2457 + mean_renyi_0: 0.0888 + mean_renyi_1: 0.0864 + mean_renyi_2: 0.0741 + mean_renyi_inf: 0.0286 + mean_spectrum_slope: 0.1323 separator_counts: mean_dot_count: 0.3510 mean_hyphen_count: 0.4708 @@ -318,7 +340,7 @@ consistent_function_style: same_concept_same_name: _doc: "The same domain concept should use the same name throughout a file — mixing `user`, `usr`, and `u` for the same thing harms readability." _languages: [elixir] - _log_baseline: -13.5397 + _log_baseline: -22.1414 brevity: mean_sample_size: -1.3457 compression: @@ -366,6 +388,13 @@ same_concept_same_name: mean_avg_line_length: 0.1837 mean_avg_sub_words_per_id: -0.1771 mean_flesch_adapted: 0.2348 + renyi_entropy: + mean_hill_1: -1.3316 + mean_hill_2: -0.5598 + mean_renyi_0: -0.2899 + mean_renyi_1: -0.3546 + mean_renyi_2: -0.1759 + mean_spectrum_slope: -0.5362 separator_counts: mean_underscore_count: -0.5711 symbol_density: diff --git a/priv/combined_metrics/dependencies.yml b/priv/combined_metrics/dependencies.yml index b8edad6..0efbed0 100644 --- a/priv/combined_metrics/dependencies.yml +++ b/priv/combined_metrics/dependencies.yml @@ -1,7 +1,7 @@ import_count_under_10: _doc: "Files should import fewer than 10 modules; high import counts signal excessive coupling." _languages: [elixir] - _log_baseline: 7.1957 + _log_baseline: 7.0143 branching: mean_branch_count: 0.2110 mean_branching_density: -1.0683 @@ -100,6 +100,14 @@ import_count_under_10: mean_flesch_adapted: -0.0204 mean_fog_adapted: 0.2028 mean_total_lines: -0.0265 + renyi_entropy: + mean_hill_1: -0.0153 + mean_hill_2: -0.0444 + mean_renyi_0: 0.0026 + mean_renyi_1: -0.0040 + mean_renyi_2: -0.0139 + mean_renyi_inf: 0.0125 + mean_spectrum_slope: 0.0397 separator_counts: mean_dot_count: -0.1234 mean_underscore_count: 0.1368 @@ -122,7 +130,7 @@ import_count_under_10: low_coupling: _doc: "Modules should depend on few external symbols — a low unique-operand count relative to total is a proxy for tight coupling." _languages: [elixir] - _log_baseline: -45.2561 + _log_baseline: -45.6308 branching: mean_branch_count: 0.0398 mean_branching_density: 0.2038 @@ -223,6 +231,13 @@ low_coupling: mean_flesch_adapted: 0.0138 mean_fog_adapted: -0.0557 mean_total_lines: -0.1476 + renyi_entropy: + mean_hill_1: -0.0660 + mean_hill_2: -0.0031 + mean_renyi_0: -0.0276 + mean_renyi_1: -0.0174 + mean_renyi_inf: 0.0132 + mean_spectrum_slope: -0.1079 separator_counts: mean_dot_count: -0.3285 mean_underscore_count: -0.1206 @@ -245,7 +260,7 @@ low_coupling: no_wildcard_imports: _doc: "Wildcard imports (`import *`, `using Module`) pollute the local namespace and hide dependencies." _languages: [elixir] - _log_baseline: -8.8366 + _log_baseline: -8.7732 branching: mean_branching_density: 0.0249 mean_non_blank_count: -0.0268 @@ -328,6 +343,11 @@ no_wildcard_imports: mean_flesch_adapted: -0.0142 mean_fog_adapted: 0.0290 mean_total_lines: -0.0268 + renyi_entropy: + mean_hill_1: 0.0090 + mean_hill_2: 0.0059 + mean_renyi_inf: 0.0160 + mean_spectrum_slope: -0.0086 separator_counts: mean_dot_count: -0.0137 mean_underscore_count: 0.0302 diff --git a/priv/combined_metrics/documentation.yml b/priv/combined_metrics/documentation.yml index e089543..2cd15d5 100644 --- a/priv/combined_metrics/documentation.yml +++ b/priv/combined_metrics/documentation.yml @@ -1,7 +1,7 @@ docstring_is_nonempty: _doc: "Docstrings must contain meaningful content, not just a placeholder or empty string." _languages: [elixir] - _log_baseline: 30.1436 + _log_baseline: 31.4876 branching: mean_branch_count: 0.3089 mean_branching_density: 0.2652 @@ -97,6 +97,14 @@ docstring_is_nonempty: mean_avg_tokens_per_line: 0.0601 mean_fog_adapted: 0.0452 mean_total_lines: 0.0437 + renyi_entropy: + mean_hill_1: 0.1612 + mean_hill_2: 0.1223 + mean_renyi_0: 0.0400 + mean_renyi_1: 0.0408 + mean_renyi_2: 0.0371 + mean_renyi_inf: 0.0460 + mean_spectrum_slope: 0.0465 separator_counts: mean_dot_count: 0.1435 mean_hyphen_count: 0.2418 @@ -122,7 +130,7 @@ docstring_is_nonempty: file_has_license_header: _doc: "Source files should begin with a license or copyright header." _languages: [elixir] - _log_baseline: 9.6516 + _log_baseline: 10.1273 branching: mean_branching_density: -0.0282 mean_non_blank_count: 0.0295 @@ -201,6 +209,14 @@ file_has_license_header: readability: mean_avg_tokens_per_line: 0.0407 mean_fog_adapted: 0.0316 + renyi_entropy: + mean_hill_1: 0.0608 + mean_hill_2: 0.0383 + mean_renyi_0: 0.0185 + mean_renyi_1: 0.0163 + mean_renyi_2: 0.0119 + mean_renyi_inf: 0.0086 + mean_spectrum_slope: 0.0349 separator_counts: mean_dot_count: 0.0772 mean_hyphen_count: 0.2471 @@ -223,7 +239,7 @@ file_has_license_header: file_has_module_docstring: _doc: "Files should have a module-level docstring explaining purpose and usage." _languages: [elixir] - _log_baseline: 24.7485 + _log_baseline: 26.0789 branching: mean_branch_count: 0.3854 mean_branching_density: -2.0000 @@ -312,6 +328,14 @@ file_has_module_docstring: mean_flesch_adapted: 0.0205 mean_fog_adapted: -0.0266 mean_total_lines: 0.0908 + renyi_entropy: + mean_hill_1: 0.1719 + mean_hill_2: 0.1063 + mean_renyi_0: 0.0457 + mean_renyi_1: 0.0441 + mean_renyi_2: 0.0333 + mean_renyi_inf: 0.0361 + mean_spectrum_slope: 0.0692 separator_counts: mean_dot_count: 0.0777 symbol_density: @@ -333,7 +357,7 @@ file_has_module_docstring: file_has_no_commented_out_code: _doc: "Files should not contain commented-out code blocks left from development." _languages: [elixir] - _log_baseline: -8.5693 + _log_baseline: -8.6108 branching: mean_branching_density: 0.0368 mean_non_blank_count: -0.0367 @@ -427,6 +451,11 @@ file_has_no_commented_out_code: mean_flesch_adapted: 0.0114 mean_fog_adapted: -0.0779 mean_total_lines: 0.0785 + renyi_entropy: + mean_hill_1: -0.0019 + mean_hill_2: -0.0065 + mean_renyi_2: -0.0019 + mean_renyi_inf: -0.0060 separator_counts: mean_dot_count: -0.0411 mean_hyphen_count: 0.1390 @@ -449,7 +478,7 @@ file_has_no_commented_out_code: function_has_docstring: _doc: "Public functions should have a docstring describing behaviour, params, and return value." _languages: [elixir] - _log_baseline: 44.4131 + _log_baseline: 46.2082 branching: mean_branch_count: 0.5279 mean_branching_density: 0.3832 @@ -553,6 +582,14 @@ function_has_docstring: mean_flesch_adapted: 0.0026 mean_fog_adapted: 0.0948 mean_total_lines: 0.1394 + renyi_entropy: + mean_hill_1: 0.2006 + mean_hill_2: 0.1795 + mean_renyi_0: 0.0557 + mean_renyi_1: 0.0517 + mean_renyi_2: 0.0548 + mean_renyi_inf: 0.0848 + mean_spectrum_slope: 0.0580 separator_counts: mean_dot_count: 0.1816 mean_hyphen_count: 0.2506 @@ -577,7 +614,7 @@ function_has_docstring: function_todo_comment_in_body: _doc: "Functions should not contain TODO/FIXME comments indicating unfinished work." _languages: [elixir] - _log_baseline: 8.1611 + _log_baseline: 8.1682 branching: mean_branch_count: -0.0287 mean_branching_density: -0.0435 @@ -670,6 +707,11 @@ function_todo_comment_in_body: mean_flesch_adapted: -0.0053 mean_fog_adapted: -0.0109 mean_total_lines: 0.0381 + renyi_entropy: + mean_hill_2: 0.0042 + mean_renyi_0: -0.0017 + mean_renyi_inf: 0.0020 + mean_spectrum_slope: -0.0082 separator_counts: mean_dot_count: 0.0485 mean_hyphen_count: 0.0317 diff --git a/priv/combined_metrics/error_handling.yml b/priv/combined_metrics/error_handling.yml index c7cc58a..159d00f 100644 --- a/priv/combined_metrics/error_handling.yml +++ b/priv/combined_metrics/error_handling.yml @@ -1,7 +1,7 @@ does_not_swallow_errors: _doc: "Errors must be handled or re-raised — empty rescue/catch blocks silently hide failures." _languages: [elixir] - _log_baseline: 91.4421 + _log_baseline: 92.2460 branching: mean_branch_count: -0.1041 mean_branching_density: -0.2095 @@ -96,6 +96,14 @@ does_not_swallow_errors: mean_flesch_adapted: -0.0373 mean_fog_adapted: 0.3019 mean_total_lines: 0.1054 + renyi_entropy: + mean_hill_1: 0.0859 + mean_hill_2: 0.0348 + mean_renyi_0: 0.0620 + mean_renyi_1: 0.0223 + mean_renyi_2: 0.0102 + mean_renyi_inf: 0.0553 + mean_spectrum_slope: 0.2146 separator_counts: mean_dot_count: 0.5172 mean_hyphen_count: 0.2483 @@ -119,7 +127,7 @@ does_not_swallow_errors: error_message_is_descriptive: _doc: "Error values should carry a meaningful message, not just a bare atom or empty string." _languages: [elixir] - _log_baseline: 55.7735 + _log_baseline: 57.6622 branching: mean_branch_count: 0.0664 mean_branching_density: -0.0540 @@ -211,6 +219,14 @@ error_message_is_descriptive: mean_flesch_adapted: -0.0175 mean_fog_adapted: 0.1420 mean_total_lines: 0.1204 + renyi_entropy: + mean_hill_1: 0.2264 + mean_hill_2: 0.1745 + mean_renyi_0: 0.0685 + mean_renyi_1: 0.0596 + mean_renyi_2: 0.0528 + mean_renyi_inf: 0.0575 + mean_spectrum_slope: 0.1093 separator_counts: mean_hyphen_count: 0.2417 mean_underscore_count: 0.2177 @@ -232,7 +248,7 @@ error_message_is_descriptive: returns_typed_error: _doc: "Functions should signal failure via a typed return (e.g. `{:error, reason}`) rather than returning `nil` or `false`." _languages: [elixir] - _log_baseline: 128.7586 + _log_baseline: 128.1260 branching: mean_branch_count: -0.1286 mean_branching_density: -0.1895 @@ -333,6 +349,14 @@ returns_typed_error: mean_flesch_adapted: -0.1272 mean_fog_adapted: 0.6637 mean_total_lines: 0.0608 + renyi_entropy: + mean_hill_1: -0.0427 + mean_hill_2: -0.1633 + mean_renyi_0: 0.0531 + mean_renyi_1: -0.0120 + mean_renyi_2: -0.0534 + mean_renyi_inf: -0.1476 + mean_spectrum_slope: 0.3019 separator_counts: mean_dot_count: 1.1292 mean_underscore_count: 0.6279 diff --git a/priv/combined_metrics/file_structure.yml b/priv/combined_metrics/file_structure.yml index 5c5e6ee..07b5e7f 100644 --- a/priv/combined_metrics/file_structure.yml +++ b/priv/combined_metrics/file_structure.yml @@ -1,7 +1,7 @@ has_consistent_indentation: _doc: "Files should use a single, consistent indentation style with no mixed tabs and spaces." _languages: [elixir] - _log_baseline: 19.8257 + _log_baseline: 20.2575 branching: mean_branch_count: 0.0582 mean_branching_density: 0.0172 @@ -108,6 +108,14 @@ has_consistent_indentation: mean_flesch_adapted: -0.0096 mean_fog_adapted: 0.0518 mean_total_lines: 0.0556 + renyi_entropy: + mean_hill_1: 0.0751 + mean_hill_2: 0.0156 + mean_renyi_0: 0.0242 + mean_renyi_1: 0.0153 + mean_renyi_2: 0.0033 + mean_renyi_inf: -0.0085 + mean_spectrum_slope: 0.1012 separator_counts: mean_dot_count: 0.2541 mean_hyphen_count: -0.0278 @@ -132,7 +140,7 @@ has_consistent_indentation: line_count_under_300: _doc: "Files should be under 300 lines; longer files typically violate single responsibility." _languages: [elixir] - _log_baseline: -49.2976 + _log_baseline: -50.0141 branching: mean_branch_count: -0.4508 mean_branching_density: -0.2446 @@ -235,6 +243,14 @@ line_count_under_300: mean_flesch_adapted: 0.0146 mean_fog_adapted: 0.0323 mean_total_lines: -0.2063 + renyi_entropy: + mean_hill_1: -0.0985 + mean_hill_2: -0.0329 + mean_renyi_0: -0.0433 + mean_renyi_1: -0.0264 + mean_renyi_2: -0.0106 + mean_renyi_inf: -0.0110 + mean_spectrum_slope: -0.1050 separator_counts: mean_dot_count: -0.0341 mean_hyphen_count: -0.2532 @@ -259,7 +275,7 @@ line_count_under_300: line_length_under_120: _doc: "Lines should be under 120 characters to avoid horizontal scrolling." _languages: [elixir] - _log_baseline: -17.3646 + _log_baseline: -17.4278 branching: mean_branching_density: -0.0814 mean_max_nesting_depth: -0.0341 @@ -358,6 +374,13 @@ line_length_under_120: mean_flesch_adapted: 0.0242 mean_fog_adapted: -0.1420 mean_total_lines: 0.0811 + renyi_entropy: + mean_hill_1: -0.0095 + mean_hill_2: 0.0072 + mean_renyi_0: -0.0107 + mean_renyi_1: -0.0024 + mean_renyi_2: 0.0020 + mean_spectrum_slope: -0.0427 separator_counts: mean_dot_count: -0.0759 mean_hyphen_count: -0.1626 @@ -380,7 +403,7 @@ line_length_under_120: no_magic_numbers: _doc: "Numeric literals should be extracted to named constants rather than used inline." _languages: [elixir] - _log_baseline: 114.2688 + _log_baseline: 116.5271 branching: mean_branch_count: -0.4352 mean_branching_density: -0.9103 @@ -473,6 +496,14 @@ no_magic_numbers: mean_flesch_adapted: -0.7069 mean_fog_adapted: 2.0000 mean_total_lines: 0.4762 + renyi_entropy: + mean_hill_1: 0.3111 + mean_hill_2: 0.1357 + mean_renyi_0: 0.0825 + mean_renyi_1: 0.0769 + mean_renyi_2: 0.0393 + mean_renyi_inf: 0.0338 + mean_spectrum_slope: 0.1938 separator_counts: mean_hyphen_count: -0.8032 mean_underscore_count: 1.7114 @@ -495,7 +526,7 @@ no_magic_numbers: single_responsibility: _doc: "Each file should have one primary concern — low complexity spread across few, focused functions." _languages: [elixir] - _log_baseline: -38.7512 + _log_baseline: -39.2930 branching: mean_branch_count: -0.0362 mean_branching_density: 0.1360 @@ -602,6 +633,14 @@ single_responsibility: mean_flesch_adapted: 0.0144 mean_fog_adapted: 0.0083 mean_total_lines: -0.1645 + renyi_entropy: + mean_hill_1: -0.0617 + mean_hill_2: -0.0455 + mean_renyi_0: -0.0255 + mean_renyi_1: -0.0178 + mean_renyi_2: -0.0156 + mean_renyi_inf: -0.0215 + mean_spectrum_slope: -0.0492 separator_counts: mean_dot_count: -0.1823 mean_hyphen_count: -0.1254 diff --git a/priv/combined_metrics/function_design.yml b/priv/combined_metrics/function_design.yml index 6bae7a6..8c8b72d 100644 --- a/priv/combined_metrics/function_design.yml +++ b/priv/combined_metrics/function_design.yml @@ -1,7 +1,7 @@ boolean_function_has_question_mark: _doc: "Functions returning a boolean should end with `?` (Elixir/Ruby) or start with `is_`/`has_` (JS/Python)." _languages: [elixir, javascript, python, ruby] - _log_baseline: -6.6664 + _log_baseline: -6.6567 brevity: mean_sample_size: 0.0127 casing_entropy: @@ -62,6 +62,9 @@ boolean_function_has_question_mark: mean_avg_tokens_per_line: 0.0248 mean_flesch_adapted: 0.0078 mean_fog_adapted: 0.0564 + renyi_entropy: + mean_renyi_0: 0.0036 + mean_spectrum_slope: 0.0238 separator_counts: mean_underscore_count: -0.2039 symbol_density: @@ -83,7 +86,7 @@ boolean_function_has_question_mark: cyclomatic_complexity_under_10: _doc: "Functions should have a cyclomatic complexity under 10." _languages: [elixir] - _log_baseline: 10.5397 + _log_baseline: 10.2807 branching: mean_branch_count: -0.2801 mean_branching_density: -0.1604 @@ -183,6 +186,14 @@ cyclomatic_complexity_under_10: mean_flesch_adapted: -0.0351 mean_fog_adapted: 0.1970 mean_total_lines: -0.1057 + renyi_entropy: + mean_hill_1: -0.0179 + mean_hill_2: -0.0532 + mean_renyi_0: 0.0061 + mean_renyi_1: -0.0049 + mean_renyi_2: -0.0178 + mean_renyi_inf: -0.0452 + mean_spectrum_slope: 0.0735 separator_counts: mean_dot_count: -0.4889 mean_hyphen_count: -0.2649 @@ -235,7 +246,7 @@ has_verb_in_name: is_less_than_20_lines: _doc: "Functions should be 20 lines or fewer." _languages: [elixir] - _log_baseline: 24.9856 + _log_baseline: 24.6810 branching: mean_branch_count: -0.0820 mean_branching_density: -0.1010 @@ -333,6 +344,14 @@ is_less_than_20_lines: mean_flesch_adapted: -0.0039 mean_fog_adapted: 0.0868 mean_total_lines: 0.0188 + renyi_entropy: + mean_hill_1: -0.0163 + mean_hill_2: -0.0495 + mean_renyi_0: 0.0035 + mean_renyi_1: -0.0041 + mean_renyi_2: -0.0142 + mean_renyi_inf: -0.0557 + mean_spectrum_slope: 0.0579 separator_counts: mean_dot_count: 0.0717 mean_hyphen_count: -0.2249 @@ -358,7 +377,7 @@ is_less_than_20_lines: nesting_depth_under_4: _doc: "Code should not nest deeper than 4 levels." _languages: [elixir] - _log_baseline: 4.7425 + _log_baseline: 4.4553 branching: mean_branch_count: -0.2707 mean_branching_density: -0.1450 @@ -456,6 +475,14 @@ nesting_depth_under_4: mean_flesch_adapted: -0.0299 mean_fog_adapted: 0.2179 mean_total_lines: -0.1171 + renyi_entropy: + mean_hill_1: -0.0294 + mean_hill_2: -0.0502 + mean_renyi_0: 0.0024 + mean_renyi_1: -0.0085 + mean_renyi_2: -0.0151 + mean_renyi_inf: -0.0060 + mean_spectrum_slope: 0.0629 separator_counts: mean_dot_count: -0.1825 mean_hyphen_count: -0.1268 @@ -480,7 +507,7 @@ nesting_depth_under_4: no_boolean_parameter: _doc: "Functions should not take boolean parameters — a flag usually means the function does two things." _languages: [elixir] - _log_baseline: 15.2442 + _log_baseline: 14.9808 branching: mean_branch_count: -2.0000 mean_branching_density: 1.0271 @@ -581,6 +608,13 @@ no_boolean_parameter: mean_flesch_adapted: -0.0254 mean_fog_adapted: 0.2928 mean_total_lines: -0.0383 + renyi_entropy: + mean_hill_1: -0.0492 + mean_hill_2: -0.0213 + mean_renyi_0: -0.0055 + mean_renyi_1: -0.0129 + mean_renyi_2: -0.0065 + mean_renyi_inf: 0.0307 separator_counts: mean_dot_count: 0.1538 mean_underscore_count: 0.2112 @@ -602,7 +636,7 @@ no_boolean_parameter: no_magic_numbers: _doc: "Numeric literals should be named constants, not inline magic numbers." _languages: [elixir] - _log_baseline: 50.9471 + _log_baseline: 51.3025 branching: mean_branch_count: -0.2708 mean_branching_density: -0.1682 @@ -693,6 +727,14 @@ no_magic_numbers: mean_flesch_adapted: -0.3819 mean_fog_adapted: 1.0656 mean_total_lines: -0.1029 + renyi_entropy: + mean_hill_1: 0.0523 + mean_hill_2: -0.0197 + mean_renyi_0: 0.0355 + mean_renyi_1: 0.0148 + mean_renyi_2: -0.0065 + mean_renyi_inf: 0.0597 + mean_spectrum_slope: 0.1368 separator_counts: mean_hyphen_count: -0.5119 mean_underscore_count: 1.2283 @@ -716,7 +758,7 @@ no_magic_numbers: parameter_count_under_4: _doc: "Functions should take fewer than 4 parameters." _languages: [elixir] - _log_baseline: 2.6289 + _log_baseline: 3.5772 branching: mean_non_blank_count: 0.0967 brevity: @@ -810,6 +852,14 @@ parameter_count_under_4: mean_flesch_adapted: 0.0271 mean_fog_adapted: -0.1290 mean_total_lines: 0.0967 + renyi_entropy: + mean_hill_1: 0.0728 + mean_hill_2: 0.1702 + mean_renyi_0: 0.0063 + mean_renyi_1: 0.0206 + mean_renyi_2: 0.0578 + mean_renyi_inf: 0.1274 + mean_spectrum_slope: -0.1199 separator_counts: mean_dot_count: 0.9099 mean_underscore_count: 0.0550 @@ -832,7 +882,7 @@ parameter_count_under_4: uses_ternary_expression: _doc: "Simple conditional assignments should use inline expressions rather than full if-blocks." _excludes_languages: [elixir] - _log_baseline: 2.1802 + _log_baseline: 2.5770 branching: mean_branch_count: -0.6221 mean_branching_density: -0.4433 @@ -934,6 +984,14 @@ uses_ternary_expression: mean_flesch_adapted: -0.0378 mean_fog_adapted: 0.2053 mean_total_lines: -0.2483 + renyi_entropy: + mean_hill_1: 0.0464 + mean_hill_2: 0.0575 + mean_renyi_0: 0.0029 + mean_renyi_1: 0.0136 + mean_renyi_2: 0.0193 + mean_renyi_inf: 0.0194 + mean_spectrum_slope: -0.0434 separator_counts: mean_dot_count: -0.4288 mean_hyphen_count: 0.7222 diff --git a/priv/combined_metrics/naming_conventions.yml b/priv/combined_metrics/naming_conventions.yml index 0675074..ad1dc57 100644 --- a/priv/combined_metrics/naming_conventions.yml +++ b/priv/combined_metrics/naming_conventions.yml @@ -1,7 +1,7 @@ class_name_is_noun: _doc: "Class and module names should be nouns describing what they represent, not verbs or gerunds." _languages: [elixir] - _log_baseline: 2.5713 + _log_baseline: 5.3672 brevity: mean_sample_size: 0.7106 compression: @@ -46,6 +46,13 @@ class_name_is_noun: mean_exclamation_density: -0.3314 readability: mean_avg_line_length: 0.1418 + renyi_entropy: + mean_hill_1: 0.4415 + mean_hill_2: 0.1396 + mean_renyi_0: 0.1716 + mean_renyi_1: 0.1236 + mean_renyi_2: 0.0441 + mean_spectrum_slope: 0.5821 symbol_density: mean_density: -0.1381 vocabulary: @@ -100,7 +107,7 @@ function_name_is_not_single_word: function_name_matches_return_type: _doc: "Functions prefixed with `get_`, `fetch_`, or `find_` should return the thing they name." _languages: [elixir] - _log_baseline: 7.8561 + _log_baseline: 7.8218 branching: mean_max_nesting_depth: 0.1335 brevity: @@ -180,6 +187,13 @@ function_name_matches_return_type: mean_avg_tokens_per_line: 0.0030 mean_flesch_adapted: -0.0107 mean_fog_adapted: 0.0058 + renyi_entropy: + mean_hill_1: 0.0053 + mean_hill_2: -0.0190 + mean_renyi_0: 0.0059 + mean_renyi_2: -0.0058 + mean_renyi_inf: -0.0108 + mean_spectrum_slope: 0.0414 separator_counts: mean_dot_count: 0.1335 mean_slash_count: 0.2282 @@ -204,7 +218,7 @@ function_name_matches_return_type: test_name_starts_with_verb: _doc: "Test descriptions should start with a verb: `creates`, `raises`, `returns`, not a noun phrase." _languages: [elixir] - _log_baseline: 8.0140 + _log_baseline: 8.8394 branching: mean_branch_count: 1.9977 mean_branching_density: 2.0000 @@ -270,6 +284,14 @@ test_name_starts_with_verb: mean_avg_line_length: 0.0943 mean_avg_tokens_per_line: 0.0600 mean_fog_adapted: 0.0600 + renyi_entropy: + mean_hill_1: 0.0982 + mean_hill_2: 0.0959 + mean_renyi_0: 0.0155 + mean_renyi_1: 0.0259 + mean_renyi_2: 0.0292 + mean_renyi_inf: 0.0267 + mean_spectrum_slope: -0.0220 symbol_density: mean_density: -0.0912 vocabulary: diff --git a/priv/combined_metrics/scope_and_assignment.yml b/priv/combined_metrics/scope_and_assignment.yml index 07096ff..11be405 100644 --- a/priv/combined_metrics/scope_and_assignment.yml +++ b/priv/combined_metrics/scope_and_assignment.yml @@ -2,7 +2,7 @@ declared_close_to_use: _doc: "Variables should be declared near their first use, not hoisted to the top of the function." _excludes_block_types: [doc] _languages: [elixir, go, javascript, python, ruby] - _log_baseline: -36.1172 + _log_baseline: -37.4837 branching: mean_branch_count: -0.2664 mean_branching_density: -0.0972 @@ -96,6 +96,14 @@ declared_close_to_use: mean_avg_tokens_per_line: -0.0827 mean_fog_adapted: -0.0557 mean_total_lines: -0.0874 + renyi_entropy: + mean_hill_1: -0.1497 + mean_hill_2: -0.1387 + mean_renyi_0: -0.0328 + mean_renyi_1: -0.0362 + mean_renyi_2: -0.0393 + mean_renyi_inf: -0.0311 + mean_spectrum_slope: -0.0120 separator_counts: mean_slash_count: -0.9706 mean_underscore_count: -0.1539 @@ -117,7 +125,7 @@ declared_close_to_use: mutated_after_initial_assignment: _doc: "Variables should not be reassigned after their initial value — prefer introducing a new name." _languages: [elixir] - _log_baseline: 6.0053 + _log_baseline: 6.0995 branching: mean_branch_count: 0.1519 mean_branching_density: 0.2073 @@ -211,6 +219,12 @@ mutated_after_initial_assignment: mean_avg_tokens_per_line: 0.0161 mean_fog_adapted: 0.0169 mean_total_lines: -0.0553 + renyi_entropy: + mean_hill_1: 0.0309 + mean_renyi_0: 0.0015 + mean_renyi_1: 0.0082 + mean_renyi_inf: -0.0351 + mean_spectrum_slope: 0.0044 separator_counts: mean_dot_count: -0.1332 mean_hyphen_count: 0.3267 @@ -235,7 +249,7 @@ mutated_after_initial_assignment: reassigned_multiple_times: _doc: "A variable reassigned many times is a sign the name is too generic or the function does too much." _languages: [elixir] - _log_baseline: -7.2135 + _log_baseline: -7.1302 branching: mean_max_nesting_depth: 0.0680 mean_non_blank_count: 0.0226 @@ -336,6 +350,14 @@ reassigned_multiple_times: mean_flesch_adapted: -0.0030 mean_fog_adapted: -0.1102 mean_total_lines: 0.0226 + renyi_entropy: + mean_hill_1: 0.0390 + mean_hill_2: -0.0240 + mean_renyi_0: 0.0072 + mean_renyi_1: 0.0105 + mean_renyi_2: -0.0073 + mean_renyi_inf: -0.0310 + mean_spectrum_slope: 0.0355 separator_counts: mean_dot_count: -0.1300 mean_hyphen_count: 0.2586 @@ -361,7 +383,7 @@ reassigned_multiple_times: scope_is_minimal: _doc: "Variables should be scoped as narrowly as possible — not declared at a wider scope than needed." _languages: [elixir] - _log_baseline: -7.8917 + _log_baseline: -7.4444 branching: mean_branch_count: -0.1072 mean_branching_density: -0.0452 @@ -455,6 +477,14 @@ scope_is_minimal: mean_flesch_adapted: 0.0319 mean_fog_adapted: 0.0564 mean_total_lines: -0.0420 + renyi_entropy: + mean_hill_1: 0.0142 + mean_hill_2: 0.1024 + mean_renyi_0: -0.0081 + mean_renyi_1: 0.0037 + mean_renyi_2: 0.0320 + mean_renyi_inf: 0.0853 + mean_spectrum_slope: -0.1027 separator_counts: mean_dot_count: 0.3290 mean_underscore_count: -0.1941 @@ -476,7 +506,7 @@ scope_is_minimal: shadowed_by_inner_scope: _doc: "Inner-scope names that shadow outer-scope names cause confusion about which value is in play." _languages: [elixir] - _log_baseline: -32.8272 + _log_baseline: -33.4708 branching: mean_branching_density: 2.0000 mean_max_nesting_depth: -0.1450 @@ -565,6 +595,14 @@ shadowed_by_inner_scope: mean_flesch_adapted: -0.0728 mean_fog_adapted: -0.1060 mean_total_lines: -0.0480 + renyi_entropy: + mean_hill_1: -0.0573 + mean_hill_2: -0.0750 + mean_renyi_0: -0.0178 + mean_renyi_1: -0.0147 + mean_renyi_2: -0.0218 + mean_renyi_inf: -0.0517 + mean_spectrum_slope: -0.0037 separator_counts: mean_dot_count: -0.0206 mean_underscore_count: 0.6826 @@ -588,7 +626,7 @@ shadowed_by_inner_scope: used_only_once: _doc: "A variable used only once is a candidate for inlining — it rarely adds clarity over a direct expression." _languages: [elixir, go, javascript, python, ruby] - _log_baseline: -38.0360 + _log_baseline: -39.0085 branching: mean_branch_count: -0.2490 mean_branching_density: -0.4526 @@ -682,6 +720,14 @@ used_only_once: mean_avg_tokens_per_line: -0.0534 mean_fog_adapted: 0.0120 mean_total_lines: -0.1653 + renyi_entropy: + mean_hill_1: -0.1214 + mean_hill_2: -0.0881 + mean_renyi_0: -0.0279 + mean_renyi_1: -0.0310 + mean_renyi_2: -0.0262 + mean_renyi_inf: -0.0226 + mean_spectrum_slope: -0.0323 separator_counts: mean_hyphen_count: 0.3212 mean_underscore_count: -0.1733 diff --git a/priv/combined_metrics/testing.yml b/priv/combined_metrics/testing.yml index e9dbf69..3069f6c 100644 --- a/priv/combined_metrics/testing.yml +++ b/priv/combined_metrics/testing.yml @@ -1,7 +1,7 @@ reasonable_test_to_code_ratio: _doc: "There should be an adequate number of test cases relative to the code being tested." _languages: [elixir] - _log_baseline: 13.2191 + _log_baseline: 13.4831 branching: mean_branch_count: 0.1869 mean_branching_density: 0.0352 @@ -102,6 +102,14 @@ reasonable_test_to_code_ratio: mean_flesch_adapted: 0.0106 mean_fog_adapted: -0.0829 mean_total_lines: 0.1973 + renyi_entropy: + mean_hill_1: 0.0127 + mean_hill_2: 0.0459 + mean_renyi_0: 0.0063 + mean_renyi_1: 0.0033 + mean_renyi_2: 0.0139 + mean_renyi_inf: 0.0256 + mean_spectrum_slope: -0.0128 separator_counts: mean_dot_count: 0.2776 mean_hyphen_count: 0.2470 @@ -126,7 +134,7 @@ reasonable_test_to_code_ratio: test_has_assertion: _doc: "Every test body must contain at least one assertion — a test without assertions proves nothing." _languages: [elixir] - _log_baseline: -10.1327 + _log_baseline: -10.4803 branching: mean_branch_count: 0.0918 mean_branching_density: 0.1642 @@ -219,6 +227,14 @@ test_has_assertion: mean_flesch_adapted: -0.0065 mean_fog_adapted: 0.0284 mean_total_lines: -0.0284 + renyi_entropy: + mean_hill_1: -0.0479 + mean_hill_2: -0.0243 + mean_renyi_0: -0.0118 + mean_renyi_1: -0.0124 + mean_renyi_2: -0.0073 + mean_renyi_inf: -0.0081 + mean_spectrum_slope: -0.0228 separator_counts: mean_dot_count: 0.0108 mean_hyphen_count: -0.1294 @@ -243,7 +259,7 @@ test_has_assertion: test_name_describes_behavior: _doc: "Test names should describe the expected behaviour, not just the method under test." _languages: [elixir] - _log_baseline: 60.4069 + _log_baseline: 61.6996 branching: mean_branch_count: 2.0000 mean_branching_density: -1.5965 @@ -326,6 +342,14 @@ test_name_describes_behavior: mean_avg_line_length: 0.1184 mean_avg_tokens_per_line: 0.0316 mean_total_lines: 0.2388 + renyi_entropy: + mean_hill_1: 0.1588 + mean_hill_2: 0.1383 + mean_renyi_0: 0.0420 + mean_renyi_1: 0.0449 + mean_renyi_2: 0.0459 + mean_renyi_inf: 0.0732 + mean_spectrum_slope: 0.0331 separator_counts: mean_dot_count: 0.0701 mean_slash_count: 1.2619 @@ -349,7 +373,7 @@ test_name_describes_behavior: test_single_concept: _doc: "Each test should verify a single concept — tests covering multiple things are harder to diagnose when they fail." _languages: [elixir] - _log_baseline: 40.5455 + _log_baseline: 40.8775 branching: mean_branch_count: 0.3696 mean_branching_density: -2.0000 @@ -451,6 +475,14 @@ test_single_concept: mean_flesch_adapted: 0.0098 mean_fog_adapted: -0.1758 mean_total_lines: 0.3391 + renyi_entropy: + mean_hill_1: 0.0253 + mean_hill_2: 0.0418 + mean_renyi_0: 0.0104 + mean_renyi_1: 0.0065 + mean_renyi_2: 0.0124 + mean_renyi_inf: 0.0303 + mean_spectrum_slope: 0.0055 separator_counts: mean_dot_count: 0.0317 mean_slash_count: 0.9554 diff --git a/priv/combined_metrics/type_and_value.yml b/priv/combined_metrics/type_and_value.yml index a54b6e6..04a59b8 100644 --- a/priv/combined_metrics/type_and_value.yml +++ b/priv/combined_metrics/type_and_value.yml @@ -1,7 +1,7 @@ boolean_assigned_from_comparison: _doc: "Boolean variables should be assigned directly from comparisons or predicate calls, not set via conditionals." _languages: [elixir] - _log_baseline: -6.7740 + _log_baseline: -6.7155 branching: mean_branch_count: -1.5392 mean_branching_density: 0.7530 @@ -110,6 +110,14 @@ boolean_assigned_from_comparison: mean_flesch_adapted: -0.0996 mean_fog_adapted: 0.2876 mean_total_lines: -0.3476 + renyi_entropy: + mean_hill_1: 0.0679 + mean_hill_2: -0.0493 + mean_renyi_0: 0.0284 + mean_renyi_1: 0.0172 + mean_renyi_2: -0.0152 + mean_renyi_inf: -0.1010 + mean_spectrum_slope: 0.2045 separator_counts: mean_dot_count: 0.3307 mean_hyphen_count: -2.0000 @@ -135,7 +143,7 @@ boolean_assigned_from_comparison: hardcoded_url_or_path: _doc: "URLs, file paths, and host names should be configuration values, not inline string literals." _languages: [elixir] - _log_baseline: 58.7858 + _log_baseline: 60.1101 branching: mean_max_nesting_depth: 0.4526 brevity: @@ -226,6 +234,14 @@ hardcoded_url_or_path: mean_avg_tokens_per_line: 0.2263 mean_flesch_adapted: -0.2352 mean_fog_adapted: 0.4805 + renyi_entropy: + mean_hill_1: 0.1232 + mean_hill_2: 0.1193 + mean_renyi_0: 0.0321 + mean_renyi_1: 0.0319 + mean_renyi_2: 0.0359 + mean_renyi_inf: 0.2124 + mean_spectrum_slope: 0.0223 separator_counts: mean_dot_count: 0.0519 mean_slash_count: -0.5872 @@ -248,7 +264,7 @@ hardcoded_url_or_path: no_empty_string_initial: _doc: "Initialising a variable to an empty string and reassigning later signals missing structure." _languages: [elixir] - _log_baseline: -5.7200 + _log_baseline: -4.9000 branching: mean_branch_count: -0.1509 mean_branching_density: -0.0146 @@ -349,6 +365,14 @@ no_empty_string_initial: mean_flesch_adapted: 0.0051 mean_fog_adapted: 0.0674 mean_total_lines: -0.0973 + renyi_entropy: + mean_hill_1: 0.0708 + mean_hill_2: 0.1388 + mean_renyi_0: 0.0055 + mean_renyi_1: 0.0194 + mean_renyi_2: 0.0457 + mean_renyi_inf: 0.0794 + mean_spectrum_slope: -0.0876 separator_counts: mean_dot_count: 0.0512 mean_hyphen_count: 0.1377 @@ -374,7 +398,7 @@ no_empty_string_initial: no_implicit_null_initial: _doc: "Initialising a variable to `nil`/`null` and assigning it later in a branch signals missing structure." _languages: [elixir] - _log_baseline: -13.1244 + _log_baseline: -12.9700 branching: mean_branch_count: -0.1283 mean_branching_density: -0.0936 @@ -480,6 +504,14 @@ no_implicit_null_initial: mean_flesch_adapted: -0.0097 mean_fog_adapted: 0.0725 mean_total_lines: -0.0648 + renyi_entropy: + mean_hill_1: 0.0195 + mean_hill_2: 0.0122 + mean_renyi_0: 0.0056 + mean_renyi_1: 0.0056 + mean_renyi_2: 0.0038 + mean_renyi_inf: 0.0103 + mean_spectrum_slope: 0.0115 separator_counts: mean_dot_count: -0.0539 mean_hyphen_count: 0.7825 @@ -505,7 +537,7 @@ no_implicit_null_initial: no_magic_value_assigned: _doc: "Literal strings and numbers assigned to variables should be named constants, not inline values." _languages: [elixir] - _log_baseline: -12.8678 + _log_baseline: -9.2288 branching: mean_branch_count: -0.2035 mean_branching_density: -0.1140 @@ -598,6 +630,13 @@ no_magic_value_assigned: mean_flesch_adapted: 0.2297 mean_fog_adapted: -0.5835 mean_total_lines: -0.0893 + renyi_entropy: + mean_hill_1: 0.3084 + mean_hill_2: 0.7145 + mean_renyi_1: 0.0837 + mean_renyi_2: 0.2504 + mean_renyi_inf: 0.3898 + mean_spectrum_slope: -0.4463 separator_counts: mean_hyphen_count: -0.5246 mean_underscore_count: -0.4596 diff --git a/priv/combined_metrics/variable_naming.yml b/priv/combined_metrics/variable_naming.yml index 5dd02ec..3e8fc11 100644 --- a/priv/combined_metrics/variable_naming.yml +++ b/priv/combined_metrics/variable_naming.yml @@ -1,7 +1,7 @@ boolean_has_is_has_prefix: _doc: "Boolean variables should be prefixed with `is_`, `has_`, or `can_`." _languages: [elixir, javascript, ruby] - _log_baseline: 22.3422 + _log_baseline: 22.7733 brevity: mean_sample_size: 0.0752 casing_entropy: @@ -51,6 +51,12 @@ boolean_has_is_has_prefix: mean_avg_sub_words_per_id: 0.3932 mean_flesch_adapted: -0.4857 mean_fog_adapted: 0.5482 + renyi_entropy: + mean_hill_1: 0.0715 + mean_hill_2: 0.0280 + mean_renyi_0: 0.0173 + mean_renyi_1: 0.0198 + mean_spectrum_slope: 0.0370 separator_counts: mean_underscore_count: 1.8116 symbol_density: @@ -69,7 +75,7 @@ boolean_has_is_has_prefix: collection_name_is_plural: _doc: "Variables holding a collection should use a plural name." _languages: [elixir, javascript, ruby] - _log_baseline: 24.4551 + _log_baseline: 21.8672 brevity: mean_sample_size: -0.5320 casing_entropy: @@ -117,6 +123,13 @@ collection_name_is_plural: mean_avg_sub_words_per_id: 0.1285 mean_flesch_adapted: -0.1311 mean_fog_adapted: 0.8035 + renyi_entropy: + mean_hill_1: -0.3788 + mean_hill_2: -0.1735 + mean_renyi_0: -0.1242 + mean_renyi_1: -0.1027 + mean_renyi_2: -0.0525 + mean_spectrum_slope: -0.3391 separator_counts: mean_underscore_count: 0.6811 symbol_density: @@ -137,7 +150,7 @@ loop_var_is_single_letter: _doc: "Loop index variables (`i`, `j`, `k`) are acceptable inside loop bodies." _excludes_block_types: [doc] _languages: [elixir, javascript, ruby] - _log_baseline: -32.8855 + _log_baseline: -33.3401 brevity: mean_sample_size: -0.1049 casing_entropy: @@ -202,6 +215,13 @@ loop_var_is_single_letter: mean_avg_tokens_per_line: 0.0100 mean_flesch_adapted: 0.4102 mean_fog_adapted: -1.3612 + renyi_entropy: + mean_hill_1: -0.0644 + mean_hill_2: -0.0329 + mean_renyi_0: -0.0244 + mean_renyi_1: -0.0181 + mean_renyi_2: -0.0096 + mean_spectrum_slope: -0.0606 separator_counts: mean_hyphen_count: 0.1363 mean_underscore_count: -1.4177 @@ -223,7 +243,7 @@ loop_var_is_single_letter: name_contains_and: _doc: "Variable names containing `and` signal a variable that holds two concerns." _languages: [elixir, javascript, ruby] - _log_baseline: -2.5335 + _log_baseline: -2.6591 branching: mean_branch_count: -0.3666 mean_branching_density: -0.3925 @@ -327,6 +347,12 @@ name_contains_and: mean_flesch_adapted: 0.3817 mean_fog_adapted: -0.9412 mean_total_lines: 0.0244 + renyi_entropy: + mean_hill_1: -0.0168 + mean_hill_2: -0.0262 + mean_renyi_2: -0.0083 + mean_renyi_inf: 0.0174 + mean_spectrum_slope: 0.0252 separator_counts: mean_dot_count: -0.2504 mean_underscore_count: -0.6180 @@ -349,7 +375,7 @@ name_contains_and: name_contains_type_suffix: _doc: "Type suffixes in names (`userString`, `nameList`) are redundant noise." _languages: [elixir, javascript, ruby] - _log_baseline: -27.7647 + _log_baseline: -28.3725 branching: mean_branch_count: -0.6581 mean_branching_density: -0.8807 @@ -420,6 +446,13 @@ name_contains_type_suffix: mean_avg_tokens_per_line: -0.0091 mean_flesch_adapted: 0.3824 mean_fog_adapted: -0.7531 + renyi_entropy: + mean_hill_1: -0.0997 + mean_hill_2: -0.0129 + mean_renyi_0: -0.0351 + mean_renyi_1: -0.0241 + mean_renyi_inf: 0.0172 + mean_spectrum_slope: -0.1195 separator_counts: mean_underscore_count: -2.0000 symbol_density: @@ -440,7 +473,7 @@ name_contains_type_suffix: name_is_abbreviation: _doc: "Abbreviated names (`usr`, `cfg`, `mgr`) reduce readability." _languages: [elixir, javascript, ruby] - _log_baseline: 21.8261 + _log_baseline: 21.9968 brevity: mean_sample_size: -0.0441 casing_entropy: @@ -510,6 +543,10 @@ name_is_abbreviation: mean_avg_tokens_per_line: -0.0896 mean_flesch_adapted: -0.0479 mean_fog_adapted: -0.0830 + renyi_entropy: + mean_hill_2: 0.0338 + mean_renyi_inf: 0.0724 + mean_spectrum_slope: -0.0538 separator_counts: mean_dot_count: -0.0928 mean_slash_count: -0.5019 @@ -532,7 +569,7 @@ name_is_abbreviation: name_is_generic: _doc: "Generic names (`data`, `result`, `tmp`, `val`, `obj`) convey no domain meaning." _languages: [elixir, javascript, ruby] - _log_baseline: 44.0574 + _log_baseline: 44.9220 branching: mean_branch_count: 0.5193 mean_branching_density: 0.3889 @@ -633,6 +670,14 @@ name_is_generic: mean_flesch_adapted: -0.2843 mean_fog_adapted: 0.5030 mean_total_lines: 0.0756 + renyi_entropy: + mean_hill_1: 0.1404 + mean_hill_2: 0.0289 + mean_renyi_0: 0.0450 + mean_renyi_1: 0.0371 + mean_renyi_2: 0.0086 + mean_renyi_inf: -0.0049 + mean_spectrum_slope: 0.1444 separator_counts: mean_slash_count: 0.9542 mean_underscore_count: 1.9344 @@ -656,7 +701,7 @@ name_is_generic: name_is_number_like: _doc: "Number-suffixed names (`var1`, `thing2`) signal a missing abstraction." _languages: [elixir, javascript, ruby] - _log_baseline: 4.1421 + _log_baseline: 3.9691 brevity: mean_sample_size: -0.0262 casing_entropy: @@ -721,6 +766,14 @@ name_is_number_like: mean_avg_tokens_per_line: -0.0075 mean_flesch_adapted: -0.1154 mean_fog_adapted: 0.0448 + renyi_entropy: + mean_hill_1: -0.0227 + mean_hill_2: -0.0138 + mean_renyi_0: -0.0054 + mean_renyi_1: -0.0056 + mean_renyi_2: -0.0042 + mean_renyi_inf: -0.0034 + mean_spectrum_slope: -0.0079 separator_counts: mean_hyphen_count: -0.5988 mean_underscore_count: 0.6819 @@ -742,7 +795,7 @@ name_is_number_like: name_is_single_letter: _doc: "Single-letter names outside loop indices are too opaque." _languages: [elixir, javascript, ruby] - _log_baseline: 30.3914 + _log_baseline: 32.2399 branching: mean_branching_density: -0.0445 mean_non_blank_count: 0.0426 @@ -825,6 +878,14 @@ name_is_single_letter: mean_flesch_adapted: -0.2349 mean_fog_adapted: -0.0666 mean_total_lines: 0.0431 + renyi_entropy: + mean_hill_1: 0.3014 + mean_hill_2: 0.1225 + mean_renyi_0: 0.0529 + mean_renyi_1: 0.0808 + mean_renyi_2: 0.0370 + mean_renyi_inf: -0.0126 + mean_spectrum_slope: 0.0926 separator_counts: mean_hyphen_count: -0.1345 mean_underscore_count: 2.0000 @@ -847,7 +908,7 @@ name_is_single_letter: name_is_too_long: _doc: "Names longer than ~30 characters harm readability." _languages: [elixir, javascript, ruby] - _log_baseline: -9.9643 + _log_baseline: -10.2893 branching: mean_branch_count: 0.0340 mean_branching_density: 0.0916 @@ -953,6 +1014,14 @@ name_is_too_long: mean_flesch_adapted: 2.0000 mean_fog_adapted: -0.3969 mean_total_lines: -0.0733 + renyi_entropy: + mean_hill_1: -0.0445 + mean_hill_2: -0.0319 + mean_renyi_0: -0.0038 + mean_renyi_1: -0.0111 + mean_renyi_2: -0.0089 + mean_renyi_inf: -0.0064 + mean_spectrum_slope: 0.0100 separator_counts: mean_dot_count: 0.0613 mean_hyphen_count: 0.1862 @@ -977,7 +1046,7 @@ name_is_too_long: name_is_too_short: _doc: "Names shorter than 3 characters (outside loops) are too opaque." _languages: [elixir, javascript, ruby] - _log_baseline: -3.7450 + _log_baseline: -5.0614 branching: mean_branch_count: -0.2327 mean_branching_density: -0.2381 @@ -1050,6 +1119,13 @@ name_is_too_short: mean_avg_line_length: 0.2778 mean_avg_tokens_per_line: -0.0329 mean_fog_adapted: -0.0263 + renyi_entropy: + mean_hill_1: -0.1805 + mean_hill_2: -0.1245 + mean_renyi_0: -0.0267 + mean_renyi_1: -0.0467 + mean_renyi_2: -0.0364 + mean_renyi_inf: -0.0138 separator_counts: mean_hyphen_count: -0.5797 mean_underscore_count: -0.1641 @@ -1072,7 +1148,7 @@ name_is_too_short: negated_boolean_name: _doc: "Negated boolean names (`isNotValid`, `notActive`) are harder to reason about." _languages: [elixir, javascript, ruby] - _log_baseline: -13.6544 + _log_baseline: -14.0280 brevity: mean_sample_size: -0.0991 casing_entropy: @@ -1139,6 +1215,13 @@ negated_boolean_name: mean_avg_tokens_per_line: -0.0494 mean_flesch_adapted: 0.2268 mean_fog_adapted: -0.7244 + renyi_entropy: + mean_hill_1: -0.0618 + mean_hill_2: -0.0191 + mean_renyi_0: -0.0208 + mean_renyi_1: -0.0164 + mean_renyi_inf: 0.0362 + mean_spectrum_slope: -0.0621 separator_counts: mean_underscore_count: -0.6273 symbol_density: @@ -1159,7 +1242,7 @@ negated_boolean_name: no_hungarian_notation: _doc: "Hungarian notation prefixes (`strName`, `bFlag`) add noise without type safety." _languages: [elixir, javascript, ruby] - _log_baseline: -8.5442 + _log_baseline: -8.4145 brevity: mean_sample_size: -0.0295 casing_entropy: @@ -1233,6 +1316,12 @@ no_hungarian_notation: mean_avg_tokens_per_line: 0.0141 mean_flesch_adapted: 0.4848 mean_fog_adapted: -0.5445 + renyi_entropy: + mean_hill_1: -0.0128 + mean_hill_2: 0.0439 + mean_renyi_2: 0.0175 + mean_renyi_inf: 0.0376 + mean_spectrum_slope: -0.0564 separator_counts: mean_dot_count: 0.0112 mean_underscore_count: -1.6136 @@ -1255,7 +1344,7 @@ no_hungarian_notation: screaming_snake_for_constants: _doc: "Module-level constants should use SCREAMING_SNAKE_CASE." _languages: [elixir, javascript, ruby] - _log_baseline: -4.5280 + _log_baseline: -4.5910 branching: mean_branching_density: 0.0176 mean_non_blank_count: -0.0180 @@ -1330,6 +1419,10 @@ screaming_snake_for_constants: mean_flesch_adapted: 0.0095 mean_fog_adapted: -0.0082 mean_total_lines: -0.0182 + renyi_entropy: + mean_hill_1: -0.0110 + mean_hill_2: -0.0036 + mean_spectrum_slope: -0.0072 separator_counts: mean_underscore_count: 0.3971 symbol_density: