Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to Coderive are documented in this file.

## [v0.8.4] - Formula Lock-In - April 12, 2026

### ✨ Major Updates
- **LinearRecurrenceFormula moved to constant-space evaluation** — Removed range-sized recurrence precomputation tables so recurrence evaluation no longer allocates memory proportional to covered range size.
- **Linear recurrence composition in NaturalArray** — `addLinearRecurrenceFormula(...)` now composes newly added formulas with existing recurrence formulas (newer-over-older), mirroring the optimized conditional formula path.
- **Recurrence lookup cache cleanup** — Removed per-index recurrence-formula index caching to avoid linear growth of recurrence metadata with random index access.

## [v0.8.0] - Fancy Machine - April 07, 2026

### ✨ Major Updates
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<div align="center">

[![Version](https://img.shields.io/badge/version-0.8.0-536af5?style=flat-square&logo=github)](https://github.com/DanexCodr/Coderive/releases)
[![Version](https://img.shields.io/badge/version-0.8.4-536af5?style=flat-square&logo=github)](https://github.com/DanexCodr/Coderive/releases)
[![Java](https://img.shields.io/badge/requires-Java%207%2B-ed8b00?style=flat-square&logo=openjdk&logoColor=white)](https://adoptium.net/)
[![License](https://img.shields.io/badge/license-MIT-f5de53?style=flat-square)](LICENSE)
[![Stars](https://img.shields.io/github/stars/DanexCodr/Coderive?style=flat-square&color=7289da&logo=github)](https://github.com/DanexCodr/Coderive/stargazers)
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.8.0
0.8.4
2 changes: 1 addition & 1 deletion docs/partials/drawer.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ <h3 class="drawer-section-title">External Links</h3>
</div>

<div class="drawer-footer">
© 2025-<span id="drawerYear"></span> DanexCodr · Coderive <span class="coderive-version">0.8.0</span>
© 2025-<span id="drawerYear"></span> DanexCodr · Coderive <span class="coderive-version">0.8.4</span>
</div>
</div>
2 changes: 1 addition & 1 deletion docs/partials/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ <h3 class="feature-name">Parameter Skipping</h3>
<!-- Footer -->
<div class="footer">
<div class="copyright">
© 2025-<span class="currentYear"></span> DanexCodr · Coderive <span class="coderive-version">0.8.0</span>
© 2025-<span class="currentYear"></span> DanexCodr · Coderive <span class="coderive-version">0.8.4</span>
</div>
<div class="footer-links">
<a href="https://github.com/DanexCodr/Coderive" target="_blank" rel="noopener">GitHub</a>
Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ warn() { echo -e " ${YELLOW}⚠${RESET} $*"; }
fail() { echo -e " ${RED}✘${RESET} $*" >&2; exit 1; }

# ── Config ───────────────────────────────────────────────────────────────────
VERSION="$(cat "$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd || pwd)/VERSION" 2>/dev/null || echo "0.8.0")"
VERSION="$(cat "$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd || pwd)/VERSION" 2>/dev/null || echo "0.8.4")"
INSTALL_DIR="${CODERIVE_HOME:-$HOME/.coderive}"
BIN_DIR="${CODERIVE_BIN:-$HOME/.local/bin}"
JAR_NAME="Coderive.jar"
Expand Down
Binary file modified source_.jar
Binary file not shown.
Binary file not shown.
43 changes: 8 additions & 35 deletions src/main/java/cod/range/NaturalArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ public class NaturalArray {
private List<SequenceFormula> sequenceFormulas = new ArrayList<SequenceFormula>();
private List<ConditionalFormula> conditionalFormulas = new ArrayList<ConditionalFormula>();
private List<LinearRecurrenceFormula> linearRecurrenceFormulas = new ArrayList<LinearRecurrenceFormula>();
private Map<Long, Integer> linearRecurrenceFormulaIndexCache = new HashMap<Long, Integer>();
private Map<Long, Object> computedCache = new HashMap<Long, Object>();

// Pending updates for lazy assignment
Expand Down Expand Up @@ -1427,17 +1426,21 @@ public void addLinearRecurrenceFormula(LinearRecurrenceFormula formula) {
ArrayTracker.recordFormulaApplication(this);
}

linearRecurrenceFormulas.add(formula);
if (linearRecurrenceFormulas.isEmpty()) {
linearRecurrenceFormulas.add(formula);
} else {
int lastIndex = linearRecurrenceFormulas.size() - 1;
LinearRecurrenceFormula current = linearRecurrenceFormulas.get(lastIndex);
LinearRecurrenceFormula merged = LinearRecurrenceFormula.compose(formula, current);
linearRecurrenceFormulas.set(lastIndex, merged);
}
clearCache();
}

public void clearCache() {
if (computedCache != null) {
computedCache.clear();
}
if (linearRecurrenceFormulaIndexCache != null) {
linearRecurrenceFormulaIndexCache.clear();
}
clearRecentCache();
lastIndex = null;
lastValue = null;
Expand Down Expand Up @@ -1507,32 +1510,6 @@ private Object evaluateConditionalFormulas(long index) {
private Object evaluateLinearRecurrenceFormulas(long index) {
if (linearRecurrenceFormulas.isEmpty()) return null;

Integer cachedFormulaIndex = linearRecurrenceFormulaIndexCache.get(index);
if (cachedFormulaIndex != null) {
int idx = cachedFormulaIndex.intValue();
if (idx >= 0 && idx < linearRecurrenceFormulas.size()) {
LinearRecurrenceFormula cachedFormula = linearRecurrenceFormulas.get(idx);
if (cachedFormula != null && cachedFormula.contains(index)) {
try {
Object cachedResult = cachedFormula.evaluate(index);
if (cachedResult != null) {
if (computedCache == null) {
computedCache = new HashMap<Long, Object>();
}
computedCache.put(index, cachedResult);
return cachedResult;
}
} catch (ProgramError e) {
throw e;
} catch (Exception e) {
throw new InternalError(
"Linear recurrence formula evaluation failed at index " + index, e);
}
}
}
linearRecurrenceFormulaIndexCache.remove(index);
}

for (int i = linearRecurrenceFormulas.size() - 1; i >= 0; i--) {
LinearRecurrenceFormula formula = linearRecurrenceFormulas.get(i);
if (formula == null) {
Expand All @@ -1547,10 +1524,6 @@ private Object evaluateLinearRecurrenceFormulas(long index) {
computedCache = new HashMap<Long, Object>();
}
computedCache.put(index, result);
if (linearRecurrenceFormulaIndexCache == null) {
linearRecurrenceFormulaIndexCache = new HashMap<Long, Integer>();
}
linearRecurrenceFormulaIndexCache.put(index, i);
}
return result;
} catch (ProgramError e) {
Expand Down
101 changes: 41 additions & 60 deletions src/main/java/cod/range/formula/LinearRecurrenceFormula.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public class LinearRecurrenceFormula {
public final AutoStackingNumber[] seedValues;
public final long seedStartIndex;
private final boolean hasConstantTerm;
private final AutoStackingNumber[] precomputedValues;
private final LinearRecurrenceFormula newerFormula;
private final LinearRecurrenceFormula olderFormula;
private static final AutoStackingNumber ZERO = AutoStackingNumber.fromLong(0L);
private static final AutoStackingNumber ONE = AutoStackingNumber.fromLong(1L);

Expand All @@ -34,19 +35,51 @@ public LinearRecurrenceFormula(
this.seedStartIndex = seedStartIndex;
this.order = coefficientsByLag != null ? coefficientsByLag.length : 0;
this.hasConstantTerm = !this.constantTerm.isZero();
this.precomputedValues = buildPrecomputedValues();
this.newerFormula = null;
this.olderFormula = null;
}

private LinearRecurrenceFormula(long start, long end,
LinearRecurrenceFormula newerFormula,
LinearRecurrenceFormula olderFormula) {
this.start = start;
this.end = end;
this.recurrenceStart = 0L;
this.order = 0;
this.coefficientsByLag = null;
this.constantTerm = ZERO;
this.seedValues = null;
this.seedStartIndex = 0L;
this.hasConstantTerm = false;
this.newerFormula = newerFormula;
this.olderFormula = olderFormula;
}

public static LinearRecurrenceFormula compose(LinearRecurrenceFormula newerFormula, LinearRecurrenceFormula olderFormula) {
if (newerFormula == null) return olderFormula;
if (olderFormula == null) return newerFormula;
long mergedStart = Math.min(newerFormula.start, olderFormula.start);
long mergedEnd = Math.max(newerFormula.end, olderFormula.end);
return new LinearRecurrenceFormula(mergedStart, mergedEnd, newerFormula, olderFormula);
}

public boolean contains(long index) {
if (isComposite()) {
return (newerFormula != null && newerFormula.contains(index))
|| (olderFormula != null && olderFormula.contains(index));
}
return index >= start && index <= end;
}

public Object evaluate(long index) {
if (precomputedValues != null) {
if (index < start || index > end) {
return null;
if (isComposite()) {
if (newerFormula != null && newerFormula.contains(index)) {
return newerFormula.evaluate(index);
}
return precomputedValues[(int) (index - start)];
if (olderFormula != null && olderFormula.contains(index)) {
return olderFormula.evaluate(index);
}
return null;
}

if (order <= 0 || seedValues == null || seedValues.length != order) {
Expand Down Expand Up @@ -98,60 +131,8 @@ public Object evaluate(long index) {
return result[0];
}

private AutoStackingNumber[] buildPrecomputedValues() {
if (order <= 0 || seedValues == null || seedValues.length != order) {
return null;
}

long total = end - start + 1L;
if (total <= 0L || total > Integer.MAX_VALUE) {
return null;
}

int size = (int) total;
AutoStackingNumber[] table = new AutoStackingNumber[size];
java.util.HashMap<Long, AutoStackingNumber> values = new java.util.HashMap<Long, AutoStackingNumber>();

for (int i = 0; i < seedValues.length; i++) {
values.put(seedStartIndex + i, seedValues[i]);
}

long computeStart = Math.min(start, seedStartIndex);
for (long idx = computeStart; idx <= end; idx++) {
AutoStackingNumber value = values.get(idx);
boolean hasIndex = values.containsKey(idx);
if (value == null && !hasIndex) {
if (idx >= recurrenceStart) {
AutoStackingNumber sum = hasConstantTerm ? constantTerm : ZERO;
for (int lag = 1; lag <= order; lag++) {
long prevIndex = idx - lag;
AutoStackingNumber prev = values.get(prevIndex);
boolean hasPrev = values.containsKey(prevIndex);
if (prev == null && !hasPrev) {
sum = null;
break;
}
AutoStackingNumber coeff = coefficientsByLag[lag - 1];
AutoStackingNumber safeCoeff = coeff != null ? coeff : ZERO;
if (prev == null) {
sum = null;
break;
}
sum = sum.add(safeCoeff.multiply(prev));
}
value = sum;
} else {
value = null;
}
values.put(idx, value);
}

if (idx >= start) {
table[(int) (idx - start)] = value;
}
}

return table;
private boolean isComposite() {
return newerFormula != null || olderFormula != null;
}

private AutoStackingNumber[][] matrixPow(AutoStackingNumber[][] base, long exp) {
Expand Down