Skip to content
Draft
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
122 changes: 122 additions & 0 deletions example/formula-calculation-engine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { Workbook } from 'exceljs';
import { ExcelCursor } from '../src/index';

// Formula calculation engine example
async function formulaCalculationExample() {
const workbook = new Workbook();
const cursor = new ExcelCursor({
workbook,
sheetName: 'Formula Calculation',
});

console.log('Creating formula calculation engine example...');

// Set up sample data
cursor
.move('A1').setData('Sales Data')
.nextRow().setData(1000) // A2
.nextRow().setData(1500) // A3
.nextRow().setData(800) // A4
.nextRow().setData(1200); // A5

// Create formulas that will be calculated
cursor
.move('C1').setData('Calculated Formulas')
.nextRow().setFormula('=A2*0.1') // C2: 10% commission
.nextRow().setFormula('=SUM(A2:A5)') // C3: Total sales
.nextRow().setFormula('=AVERAGE(A2:A5)') // C4: Average sales
.nextRow().setFormula('=MAX(A2:A5)') // C5: Best sale
.nextRow().setFormula('=MIN(A2:A5)') // C6: Worst sale
.nextRow().setFormula('=COUNT(A2:A5)') // C7: Count of sales
.nextRow().setFormula('=C3*0.05'); // C8: 5% bonus on total

// Add labels
cursor
.move('B1').setData('Descriptions')
.nextRow().setData('Commission (10%):')
.nextRow().setData('Total Sales:')
.nextRow().setData('Average Sale:')
.nextRow().setData('Best Sale:')
.nextRow().setData('Worst Sale:')
.nextRow().setData('Sale Count:')
.nextRow().setData('Bonus (5% of total):');

console.log('\n=== Demonstrating Formula Calculation Engine ===');

// Test direct formula calculation
console.log('Direct calculation of "100*2":', cursor.calculateFormula('100*2'));
console.log('Direct calculation of "SUM(A2:A5)":', cursor.calculateFormula('SUM(A2:A5)'));

// Calculate values for formula cells
console.log('\n=== Calculated Values ===');
console.log('C2 (Commission):', cursor.getCalculatedValue('C2'));
console.log('C3 (Total Sales):', cursor.getCalculatedValue('C3'));
console.log('C4 (Average):', cursor.getCalculatedValue('C4'));
console.log('C5 (Max):', cursor.getCalculatedValue('C5'));
console.log('C6 (Min):', cursor.getCalculatedValue('C6'));
console.log('C7 (Count):', cursor.getCalculatedValue('C7'));
console.log('C8 (Bonus):', cursor.getCalculatedValue('C8'));

// Show how cached results work
console.log('\n=== Showing Cached Results ===');
const formulaInfo = cursor.processFormulaCell('C3');
console.log('C3 formula info after calculation:', formulaInfo);

// Add calculated results to spreadsheet for visualization
cursor
.move('D1').setData('Calculated Results')
.nextRow().setData(cursor.getCalculatedValue('C2'))
.nextRow().setData(cursor.getCalculatedValue('C3'))
.nextRow().setData(cursor.getCalculatedValue('C4'))
.nextRow().setData(cursor.getCalculatedValue('C5'))
.nextRow().setData(cursor.getCalculatedValue('C6'))
.nextRow().setData(cursor.getCalculatedValue('C7'))
.nextRow().setData(cursor.getCalculatedValue('C8'));

// Test bulk calculation
console.log('\n=== Testing Bulk Formula Calculation ===');

// Create some more formulas
cursor
.move('F1').setData('More Formulas')
.nextRow().setFormula('=A2+A3')
.nextRow().setFormula('=A4-A5')
.nextRow().setFormula('=SUM(A2,A4)');

console.log('Before bulk calculation:');
console.log('F2 has result:', cursor.processFormulaCell('F2').hasResult);
console.log('F3 has result:', cursor.processFormulaCell('F3').hasResult);
console.log('F4 has result:', cursor.processFormulaCell('F4').hasResult);

// Calculate all formulas at once
cursor.calculateAllFormulas();

console.log('After bulk calculation:');
console.log('F2 result:', cursor.processFormulaCell('F2').result);
console.log('F3 result:', cursor.processFormulaCell('F3').result);
console.log('F4 result:', cursor.processFormulaCell('F4').result);

// Demonstrate error handling
console.log('\n=== Error Handling ===');
cursor.move('G1').setFormula('=UNSUPPORTED_FUNCTION(A1)');
const errorValue = cursor.getCalculatedValue('G1');
console.log('Result of unsupported function:', errorValue);

// Save the file
await workbook.xlsx.writeFile('./result/formula-calculation-engine.xlsx');
console.log('\n✅ Formula calculation engine example saved to ./result/formula-calculation-engine.xlsx');

// Display a summary
console.log('\n=== Summary ===');
console.log('The formula calculation engine can:');
console.log('1. Calculate basic arithmetic: +, -, *, /');
console.log('2. Handle cell references: A1, B2, etc.');
console.log('3. Process Excel functions: SUM, AVERAGE, MAX, MIN, COUNT, COUNTA');
console.log('4. Handle cell ranges: A1:A5');
console.log('5. Cache calculated results automatically');
console.log('6. Calculate individual formulas or all at once');
console.log('7. Handle errors gracefully');
}

// Run the example
formulaCalculationExample().catch(console.error);
56 changes: 56 additions & 0 deletions example/formula-dependency-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Workbook } from 'exceljs';
import { ExcelCursor } from '../src/index';

// Test the fix for formula dependency calculation
async function testFormulaDependency() {
const workbook = new Workbook();
const cursor = new ExcelCursor({
workbook,
sheetName: 'Formula Dependency Test',
});

console.log('=== Testing Formula Dependency Fix ===');

// Create a chain of dependent formulas
cursor
.move('A1').setData(100) // Base value
.move('B1').setFormula('=A1*0.1') // 10% of A1 = 10
.move('C1').setFormula('=B1+5') // B1 + 5 = 15
.move('D1').setFormula('=C1*2'); // C1 * 2 = 30

console.log('Chain of formulas:');
console.log('A1 (base):', cursor.getCellValue('A1'));
console.log('B1 (=A1*0.1):', cursor.getCalculatedValue('B1'));
console.log('C1 (=B1+5):', cursor.getCalculatedValue('C1'));
console.log('D1 (=C1*2):', cursor.getCalculatedValue('D1'));

// Test SUM with mixed formula cells
cursor.move('E1').setFormula('=SUM(A1:D1)'); // Should be 100+10+15+30 = 155
console.log('E1 (=SUM(A1:D1)):', cursor.getCalculatedValue('E1'));

// Test complex scenario
cursor
.move('A2').setFormula('=10*5') // 50
.move('B2').setFormula('=A2/2') // 25
.move('C2').setFormula('=B2+A2') // 75
.move('D2').setFormula('=SUM(A2:C2)'); // 150

console.log('\nComplex formula chain:');
console.log('A2 (=10*5):', cursor.getCalculatedValue('A2'));
console.log('B2 (=A2/2):', cursor.getCalculatedValue('B2'));
console.log('C2 (=B2+A2):', cursor.getCalculatedValue('C2'));
console.log('D2 (=SUM(A2:C2)):', cursor.getCalculatedValue('D2'));

// Verify that results are cached
console.log('\nVerifying cached results:');
const b1Info = cursor.processFormulaCell('B1');
const c1Info = cursor.processFormulaCell('C1');
console.log('B1 has cached result:', b1Info.hasResult, 'Value:', b1Info.result);
console.log('C1 has cached result:', c1Info.hasResult, 'Value:', c1Info.result);

// Save file
await workbook.xlsx.writeFile('./result/formula-dependency-test.xlsx');
console.log('\n✅ Formula dependency test saved to ./result/formula-dependency-test.xlsx');
}

testFormulaDependency().catch(console.error);
80 changes: 80 additions & 0 deletions example/formula-processing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Workbook } from 'exceljs';
import { ExcelCursor } from '../src/index';

// Formula processing example showing the new utilities
async function formulaProcessingExample() {
const workbook = new Workbook();
const cursor = new ExcelCursor({
workbook,
sheetName: 'Formula Processing',
});

console.log('Creating formula processing example...');

// Set up some sample data
cursor
.move('A1').setData('Numbers')
.nextRow().setData(100)
.nextRow().setData(200)
.nextRow().setData(300);

// Add some formulas
cursor
.move('C1').setData('Formulas')
.nextRow().setFormula('=A2*2') // Simple arithmetic
.nextRow().setFormula('=SUM(A2:A4)') // SUM function
.nextRow().setFormula('=AVERAGE(A2:A4)'); // AVERAGE function

// Add some analysis using the new methods
cursor
.move('E1').setData('Analysis')
.nextRow().setData('Is C2 formula?')
.nextCol().setData(cursor.isFormulaCell('C2'))

.nextRow().goBackToFirstCollumn().nextCol(4)
.setData('C2 formula:')
.nextCol().setData(cursor.getFormula('C2') || 'None')

.nextRow().goBackToFirstCollumn().nextCol(4)
.setData('C2 raw value:')
.nextCol().setData(JSON.stringify(cursor.getCellValue('C2')));

// Demonstrate processing different types of cells
console.log('\n=== Processing Different Cell Types ===');

// Regular cell
const regularCellInfo = cursor.processFormulaCell('A2');
console.log('A2 (number):', regularCellInfo);

// Formula cells
const formulaCell1 = cursor.processFormulaCell('C2');
console.log('C2 (formula):', formulaCell1);

const formulaCell2 = cursor.processFormulaCell('C3');
console.log('C3 (formula):', formulaCell2);

// Simulate having calculated results (normally done by Excel)
console.log('\n=== Simulating Calculated Results ===');
const worksheet = cursor.getWorkbook().getWorksheet('Formula Processing');
worksheet.getCell('C2').value = { formula: '=A2*2', result: 200 };
worksheet.getCell('C3').value = { formula: '=SUM(A2:A4)', result: 600 };

// Check the processed results with calculated values
const withResult1 = cursor.processFormulaCell('C2');
console.log('C2 (with result):', withResult1);

const withResult2 = cursor.processFormulaCell('C3');
console.log('C3 (with result):', withResult2);

// Show difference between raw value and processed value
console.log('\n=== Value Comparison ===');
console.log('C2 getCellValue():', cursor.getCellValue('C2'));
console.log('C2 getFormulaCellValue():', cursor.getFormulaCellValue('C2'));

// Save the file
await workbook.xlsx.writeFile('./result/formula-processing.xlsx');
console.log('\n✅ Formula processing example saved to ./result/formula-processing.xlsx');
}

// Run the example
formulaProcessingExample().catch(console.error);
92 changes: 92 additions & 0 deletions example/recursive-formulas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Workbook } from 'exceljs';
import { ExcelCursor } from '../src/index';

// Demonstrate the enhanced recursive formula calculation
async function recursiveFormulaExample() {
const workbook = new Workbook();
const cursor = new ExcelCursor({
workbook,
sheetName: 'Recursive Formulas',
});

console.log('=== Enhanced Recursive Formula Calculation ===');

// Create a complex dependency chain
cursor
.move('A1').setData('Base Values')
.nextCol().setData(100) // B1 = 100
.nextCol().setData(50); // C1 = 50

// Create formulas that depend on each other in a chain
cursor
.move('A2').setData('Level 1:')
.nextCol().setFormula('=B1*0.1') // B2 = B1 * 0.1 = 10
.nextCol().setFormula('=C1*0.2'); // C2 = C1 * 0.2 = 10

cursor
.move('A3').setData('Level 2:')
.nextCol().setFormula('=B2+C2') // B3 = B2 + C2 = 20 (depends on B2 and C2)
.nextCol().setFormula('=B3*2'); // C3 = B3 * 2 = 40 (depends on B3)

cursor
.move('A4').setData('Level 3:')
.nextCol().setFormula('=SUM(B1:C3)') // B4 = SUM of all above
.nextCol().setFormula('=AVERAGE(B1:C3)'); // C4 = AVERAGE of all above

// Demonstrate that calculating one formula recursively calculates all dependencies
console.log('\n1. Before any calculations - checking cache status:');
console.log('B2 (Level 1) has result:', cursor.processFormulaCell('B2').hasResult);
console.log('C2 (Level 1) has result:', cursor.processFormulaCell('C2').hasResult);
console.log('B3 (Level 2) has result:', cursor.processFormulaCell('B3').hasResult);
console.log('C3 (Level 2) has result:', cursor.processFormulaCell('C3').hasResult);
console.log('B4 (Level 3) has result:', cursor.processFormulaCell('B4').hasResult);
console.log('C4 (Level 3) has result:', cursor.processFormulaCell('C4').hasResult);

console.log('\n2. Calculating B4 (which depends on all other formulas):');
const b4Result = cursor.getCalculatedValue('B4');
console.log('B4 calculated value:', b4Result);

console.log('\n3. After calculating B4 - all dependencies should now be cached:');
console.log('B2 (Level 1) has result:', cursor.processFormulaCell('B2').hasResult, '- Value:', cursor.processFormulaCell('B2').result);
console.log('C2 (Level 1) has result:', cursor.processFormulaCell('C2').hasResult, '- Value:', cursor.processFormulaCell('C2').result);
console.log('B3 (Level 2) has result:', cursor.processFormulaCell('B3').hasResult, '- Value:', cursor.processFormulaCell('B3').result);
console.log('C3 (Level 2) has result:', cursor.processFormulaCell('C3').hasResult, '- Value:', cursor.processFormulaCell('C3').result);
console.log('B4 (Level 3) has result:', cursor.processFormulaCell('B4').hasResult, '- Value:', cursor.processFormulaCell('B4').result);

console.log('\n4. Now calculating C4 should be fast (uses cached dependencies):');
const c4Result = cursor.getCalculatedValue('C4');
console.log('C4 calculated value:', c4Result);
console.log('C4 has result:', cursor.processFormulaCell('C4').hasResult, '- Value:', cursor.processFormulaCell('C4').result);

// Demonstrate circular reference handling
console.log('\n5. Testing circular reference handling:');
cursor.move('D1').setFormula('=D2+1');
cursor.move('D2').setFormula('=D1+1');

const d1Result = cursor.getCalculatedValue('D1');
const d2Result = cursor.getCalculatedValue('D2');
console.log('D1 (circular) result:', d1Result);
console.log('D2 (circular) result:', d2Result);

// Add verification data to the spreadsheet
cursor
.move('A6').setData('Verification:')
.nextRow().setData('B2 calculated:').nextCol().setData(cursor.getCalculatedValue('B2'))
.nextRow().setData('C2 calculated:').nextCol().setData(cursor.getCalculatedValue('C2'))
.nextRow().setData('B3 calculated:').nextCol().setData(cursor.getCalculatedValue('B3'))
.nextRow().setData('C3 calculated:').nextCol().setData(cursor.getCalculatedValue('C3'))
.nextRow().setData('B4 calculated:').nextCol().setData(cursor.getCalculatedValue('B4'))
.nextRow().setData('C4 calculated:').nextCol().setData(cursor.getCalculatedValue('C4'));

// Save the file
await workbook.xlsx.writeFile('./result/recursive-formulas.xlsx');
console.log('\n✅ Recursive formula example saved to ./result/recursive-formulas.xlsx');

console.log('\n=== Summary ===');
console.log('✅ Recursive calculation: When calculating a formula, all its dependencies are automatically calculated and cached');
console.log('✅ Circular reference detection: Prevents infinite recursion');
console.log('✅ Performance optimization: Calculated results are cached to avoid recalculation');
console.log('✅ Deep dependency chains: Handles complex multi-level formula dependencies');
}

recursiveFormulaExample().catch(console.error);
Loading