Skip to content

compiler: closed-form Enum.sum for stepped ranges (O(1) vs O(n))#115

Merged
yevbar merged 1 commit intomasterfrom
happy/closed-form-stepped-enum-sum
Mar 1, 2026
Merged

compiler: closed-form Enum.sum for stepped ranges (O(1) vs O(n))#115
yevbar merged 1 commit intomasterfrom
happy/closed-form-stepped-enum-sum

Conversation

@yevbar
Copy link
Copy Markdown
Contributor

@yevbar yevbar commented Mar 1, 2026

Summary

Use the arithmetic progression formula S = n*(first+last)/2 for Enum.sum(start..stop//step) when step is a constant integer.

Problem

Previously, Enum.sum(start..stop//step) with non-unit step fell back to an O(n) reduce loop, generating a helper function and iterating over every element.

Solution

Apply the closed-form arithmetic progression formula for all constant-step ranges:

count = max(div(stop - start, step) + 1, 0)
last  = start + (count - 1) * step
sum   = count * (start + last) / 2

This computes the result in O(1) — no loop, no helper function.

Examples

  • Enum.sum(1..9//2) → 1+3+5+7+9 = 25 (was O(n) loop, now O(1))
  • Enum.sum(0..12//3) → 0+3+6+9+12 = 30 (was O(n) loop, now O(1))

Correctness

The integer division by 2 is exact because for any arithmetic progression, either the count or (first+last) is always even.

What's unchanged

  • Enum.sum(start..stop) (step=1) still uses its existing closed-form
  • Non-constant step expressions still fall back to reduce loops

Tests

  • Updated existing test to verify no loop helper is generated for constant step
  • Added 3 new tests: WAT generation, constant folding for step=2 (→25), step=3 (→30)
  • All 2461 compiler tests pass, all 9393 total tests pass

Use the arithmetic progression formula S = n*(first+last)/2 for
Enum.sum(start..stop//step) when step is a constant integer.

Previously, non-unit step ranges fell back to an O(n) reduce loop.
Now all constant-step ranges use the O(1) closed-form formula:
  count = max(div(stop - start, step) + 1, 0)
  last  = start + (count - 1) * step
  sum   = count * (start + last) / 2

Examples:
  Enum.sum(1..9//2)   # 1+3+5+7+9 = 25 (was loop, now O(1))
  Enum.sum(0..12//3)  # 0+3+6+9+12 = 30 (was loop, now O(1))

The integer division by 2 is exact because for any arithmetic
progression, either count or (first+last) is always even.

Non-constant step expressions still fall back to reduce loops.
@yevbar yevbar merged commit a1c3a38 into master Mar 1, 2026
4 checks passed
@yevbar yevbar deleted the happy/closed-form-stepped-enum-sum branch March 1, 2026 08:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant