Functional Scope (In-Scope)
- Dynamic Formula Resolution: Evaluating raw strings starting with
=supporting coordinate addition and functions (e.g.SUM). - Cell Reference Ranges: Expanding rectangular grid bounding coordinates like
A1:B3to discrete coordinate sequences during dependency evaluation. - Strict Circular Reference Blocking: Intercepting circular loops via DFS checking before allowing any formula insertion.
- Sub-Graph Incremental Recalculation: Minimizing overhead by only re-evaluating cells downstream from modified dependencies.
- Error Cascade Propagation: Instantly bubbling standard spreadsheet error conditions like
#CIRC!or#REF!to all dependent downstream cell configurations.
Explicit Boundaries (Out-of-Scope)
- Advanced Math Functions: Parsing external trigonometry modules, complex statistics formulas, or multi-argument logical operators (e.g. IF/VLOOKUP).
- Physical Rendering Graphics: Canvas/WebGl layouts, cell border rendering, custom fonts, or CSV/XLSX file format encoders.
Clean reference designs demonstrating pre-order topological evaluation and cycle blocking in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.*;
class Cell {
private final String name;
private String rawInput;
// Either Double OR String error code
private Object computedValue;
// Cells THIS cell depends on
private final Set<String> dependencies = new HashSet<>();
// Cells depending on THIS cell
private final Set<String> dependents = new HashSet<>();
public Cell(String name) {
this.name = name;
this.rawInput = "";
this.computedValue = 0.0;
}
public String getName() {
return name;
}
public String getRawInput() {
return rawInput;
}
public void setRawInput(String rawInput) {
this.rawInput = rawInput;
}
public Object getComputedValue() {
return computedValue;
}
public void setComputedValue(Object computedValue) {
this.computedValue = computedValue;
}
public Set<String> getDependencies() {
return dependencies;
}
public Set<String> getDependents() {
return dependents;
}
}
class Spreadsheet {
private final Map<String, Cell> cells = new HashMap<>();
private final ReentrantReadWriteLock rwLock =
new ReentrantReadWriteLock();
public Cell getOrCreateCell(String name) {
return cells.computeIfAbsent(name, Cell::new);
}
public void setCell(String name, String input) throws Exception {
rwLock.writeLock().lock();
try {
Cell cell = getOrCreateCell(name);
Set<String> oldDeps =
new HashSet<>(cell.getDependencies());
Set<String> newDeps =
parseDependencies(input);
// TEMPORARILY update deps for cycle check
cell.getDependencies().clear();
cell.getDependencies().addAll(newDeps);
if (hasCycle(name)) {
// revert
cell.getDependencies().clear();
cell.getDependencies().addAll(oldDeps);
throw new IllegalArgumentException(
"Circular dependency detected for cell: " + name
);
}
// cleanup old relationships
for (String oldDep : oldDeps) {
if (!newDeps.contains(oldDep)) {
Cell depCell = cells.get(oldDep);
if (depCell != null) {
depCell.getDependents().remove(name);
}
}
}
// add new relationships
for (String newDep : newDeps) {
if (!oldDeps.contains(newDep)) {
getOrCreateCell(newDep)
.getDependents()
.add(name);
}
}
cell.setRawInput(input);
recalculate(name);
} finally {
rwLock.writeLock().unlock();
}
}
public Object getCellValue(String name) {
rwLock.readLock().lock();
try {
Cell cell = cells.get(name);
if (cell == null) {
return 0.0;
}
return cell.getComputedValue();
} finally {
rwLock.readLock().unlock();
}
}
// ─────────────────────────────────────────────────────────────
private Set<String> parseDependencies(String input) {
Set<String> deps = new HashSet<>();
if (input == null || !input.startsWith("=")) {
return deps;
}
Pattern rangePattern =
Pattern.compile("([A-Z]+[0-9]+):([A-Z]+[0-9]+)");
Matcher rangeMatcher =
rangePattern.matcher(input);
while (rangeMatcher.find()) {
deps.addAll(
expandRange(
rangeMatcher.group(1),
rangeMatcher.group(2)
)
);
}
Pattern cellPattern =
Pattern.compile("\\b([A-Z]+[0-9]+)\\b");
Matcher cellMatcher =
cellPattern.matcher(
input.replaceAll(
"([A-Z]+[0-9]+):([A-Z]+[0-9]+)",
""
)
);
while (cellMatcher.find()) {
deps.add(cellMatcher.group(1));
}
return deps;
}
private List<String> expandRange(String start, String end) {
List<String> list = new ArrayList<>();
String startColStr =
start.replaceAll("[0-9]", "");
String endColStr =
end.replaceAll("[0-9]", "");
int startRow =
Integer.parseInt(
start.replaceAll("[A-Z]", "")
);
int endRow =
Integer.parseInt(
end.replaceAll("[A-Z]", "")
);
int startCol =
columnToNumber(startColStr);
int endCol =
columnToNumber(endColStr);
int minCol = Math.min(startCol, endCol);
int maxCol = Math.max(startCol, endCol);
int minRow = Math.min(startRow, endRow);
int maxRow = Math.max(startRow, endRow);
for (int col = minCol; col <= maxCol; col++) {
String colName = numberToColumn(col);
for (int row = minRow; row <= maxRow; row++) {
list.add(colName + row);
}
}
return list;
}
private int columnToNumber(String col) {
int result = 0;
for (int i = 0; i < col.length(); i++) {
result =
result * 26 +
(col.charAt(i) - 'A' + 1);
}
return result;
}
private String numberToColumn(int num) {
StringBuilder sb = new StringBuilder();
while (num > 0) {
num--;
sb.append(
(char) ('A' + (num % 26))
);
num /= 26;
}
return sb.reverse().toString();
}
// ─────────────────────────────────────────────────────────────
private boolean hasCycle(String startCell) {
Set<String> visited = new HashSet<>();
Set<String> stack = new HashSet<>();
return dfsCheck(startCell, visited, stack);
}
private boolean dfsCheck(
String curr,
Set<String> visited,
Set<String> stack
) {
if (stack.contains(curr)) {
return true;
}
if (visited.contains(curr)) {
return false;
}
visited.add(curr);
stack.add(curr);
Cell cell = cells.get(curr);
if (cell != null) {
for (String dep : cell.getDependencies()) {
if (dfsCheck(dep, visited, stack)) {
return true;
}
}
}
stack.remove(curr);
return false;
}
// ─────────────────────────────────────────────────────────────
private void recalculate(String startCell) {
List<String> order =
getTopologicalOrder(startCell);
for (String cellName : order) {
evaluateCell(cells.get(cellName));
}
}
private List<String> getTopologicalOrder(String start) {
List<String> order = new ArrayList<>();
Set<String> visited = new HashSet<>();
dfsTopologicalSort(start, visited, order);
return order;
}
private void dfsTopologicalSort(
String curr,
Set<String> visited,
List<String> order
) {
visited.add(curr);
order.add(curr); // pre-order: add BEFORE visiting dependents
Cell cell = cells.get(curr);
if (cell != null) {
for (String dep : cell.getDependents()) {
if (!visited.contains(dep)) {
dfsTopologicalSort(
dep,
visited,
order
);
}
}
}
}
// ─────────────────────────────────────────────────────────────
private void evaluateCell(Cell cell) {
String input = cell.getRawInput();
if (input == null || input.isEmpty()) {
cell.setComputedValue(0.0);
return;
}
// RAW VALUE
if (!input.startsWith("=")) {
try {
cell.setComputedValue(
Double.parseDouble(input)
);
} catch (NumberFormatException e) {
cell.setComputedValue("#VAL!");
}
return;
}
// FORMULA
String formula =
input.substring(1).trim();
try {
// SUM(A1:C5)
if (formula.startsWith("SUM(")) {
String range =
formula.substring(
4,
formula.length() - 1
);
String[] parts =
range.split(":");
List<String> rangeCells =
expandRange(parts[0], parts[1]);
double sum = 0;
for (String rc : rangeCells) {
Object val = getCellValue(rc);
if (val instanceof String) {
cell.setComputedValue(val);
return;
}
sum += (Double) val;
}
cell.setComputedValue(sum);
} else {
// A1+B1+10
String[] parts =
formula.split("\\+");
double sum = 0;
for (String part : parts) {
part = part.trim();
if (
part.matches("[A-Z]+[0-9]+")
) {
Object val =
getCellValue(part);
if (val instanceof String) {
cell.setComputedValue(val);
return;
}
sum += (Double) val;
} else {
sum += Double.parseDouble(part);
}
}
cell.setComputedValue(sum);
}
} catch (Exception e) {
cell.setComputedValue("#REF!");
}
}
}
// ─────────────────────────────────────────────────────────────
public class Main {
public static void main(String[] args)
throws Exception {
System.out.println(
"=== STARTING SPREADSHEET ENGINE DEMO ==="
);
Spreadsheet sheet =
new Spreadsheet();
// ─────────────────────────────────────────
// BASIC VALUES
// ─────────────────────────────────────────
sheet.setCell("A1", "10");
sheet.setCell("B1", "20");
System.out.println(
"A1: " + sheet.getCellValue("A1") +
", B1: " + sheet.getCellValue("B1")
);
// ─────────────────────────────────────────
// SIMPLE FORMULA
// ─────────────────────────────────────────
sheet.setCell("C1", "=A1+B1");
System.out.println(
"Formula C1 (=A1+B1) resolved to: " +
sheet.getCellValue("C1")
);
// ─────────────────────────────────────────
// RANGE SUM
// ─────────────────────────────────────────
sheet.setCell("D1", "=SUM(A1:C1)");
System.out.println(
"Formula D1 (=SUM(A1:C1)) resolved to: " +
sheet.getCellValue("D1")
);
// ─────────────────────────────────────────
// CASCADE UPDATE
// ─────────────────────────────────────────
System.out.println(
"\n--- Changing cell A1 to 150 ---"
);
sheet.setCell("A1", "150");
System.out.println(
"C1 (expected 170): " +
sheet.getCellValue("C1")
);
System.out.println(
"D1 (expected 200): " +
sheet.getCellValue("D1")
);
// ─────────────────────────────────────────
// MULTI LETTER COLUMN TEST
// ─────────────────────────────────────────
sheet.setCell("AA1", "5");
sheet.setCell("AB1", "15");
sheet.setCell("AC1", "=AA1+AB1");
System.out.println(
"\nAC1 (expected 20): " +
sheet.getCellValue("AC1")
);
sheet.setCell("AD1", "=SUM(AA1:AC1)");
System.out.println(
"AD1 (expected 40): " +
sheet.getCellValue("AD1")
);
// ─────────────────────────────────────────
// CIRCULAR DEPENDENCY TEST
// ─────────────────────────────────────────
System.out.println(
"\n--- Attempting Circular Dependency ---"
);
try {
sheet.setCell("C1", "=D1+10");
} catch (IllegalArgumentException e) {
System.out.println(
"[REJECTED SUCCESSFULLY] " +
e.getMessage()
);
}
System.out.println(
"C1 state after rejection: " +
sheet.getCellValue("C1")
);
System.out.println(
"\n=== SIMULATION COMPLETE ==="
);
}
}