4. Core Features & Functionalities
4.1 Denomination Calculation Engine
4.1.1 Core Requirements
The calculation engine is the heart of the system and has the following strict requirements:
- MUST use arbitrary precision arithmetic (Decimal type)
- MUST NOT have rounding errors or precision loss
- MUST complete calculations in < 100ms for amounts up to 1 trillion
- MUST support 4 base currencies: INR, USD, EUR, GBP
- MUST return breakdown with notes + coins separately
Input Specification
interface CalculationRequest {
amount: string; // Required - "1000" or "1000.50"
currency: string; // Required - "INR" | "USD" | "EUR" | "GBP"
optimization_mode?: string; // Optional - default: "greedy"
save_to_history?: boolean; // Optional - default: true
}
Output Specification
interface CalculationResponse {
amount: string;
currency: string;
optimization_mode: string;
total_notes: number;
total_coins: number;
total_denominations: number;
breakdowns: DenominationBreakdown[];
calculation_time_ms: number;
saved_to_history: boolean;
calculation_id?: number;
}
interface DenominationBreakdown {
denomination: number;
type: "note" | "coin";
count: number;
total_value: string;
}
Example Calculation
{
"amount": "50000",
"currency": "INR",
"optimization_mode": "greedy",
"total_notes": 25,
"total_coins": 0,
"total_denominations": 1,
"breakdowns": [
{
"denomination": 2000,
"type": "note",
"count": 25,
"total_value": "50000"
}
],
"calculation_time_ms": 2.4,
"saved_to_history": true,
"calculation_id": 42
}
4.1.2 Currency Support
| Currency | Code | Symbol | Notes | Coins | Smallest Unit |
|---|---|---|---|---|---|
| Indian Rupee | INR | ? | 2000, 500, 200, 100, 50, 20, 10 | 10, 5, 2, 1 | 0.01 (paisa) |
| US Dollar | USD | $ | 100, 50, 20, 10, 5, 2, 1 | 1, 0.50, 0.25, 0.10, 0.05, 0.01 | 0.01 (cent) |
| Euro | EUR | € | 500, 200, 100, 50, 20, 10, 5 | 2, 1, 0.50, 0.20, 0.10, 0.05, 0.02, 0.01 | 0.01 (cent) |
| British Pound | GBP | £ | 50, 20, 10, 5 | 2, 1, 0.50, 0.20, 0.10, 0.05, 0.02, 0.01 | 0.01 (penny) |
Currency Configuration
Location: packages/core-engine/config/currencies.json
{
"INR": {
"name": "Indian Rupee",
"symbol": "?",
"code": "INR",
"notes": [2000, 500, 200, 100, 50, 20, 10],
"coins": [10, 5, 2, 1],
"decimal_places": 2,
"smallest_unit": 0.01
}
}
Adding New Currency
- Add configuration to
currencies.json - Add FX rate to
fx_rates_cache.json - Update TypeScript types if needed
- No code changes required (configuration-driven)
4.1.3 Optimization Modes
Mode 1: Greedy (Default)
- Goal: Minimize total number of notes + coins
- Algorithm: Always use largest denomination first
- Use Case: Default mode for most scenarios
- Performance: O(n) where n = number of denominations
Example:
Amount: ?50,000
Result: 25 × ?2000 = ?50,000
Total: 25 denominations
Mode 2: Balanced
- Goal: Even distribution across denominations
- Algorithm: Distribute amount more evenly
- Use Case: When you want variety in denominations
- Performance: O(n)
Example:
Amount: ?50,000
Result:
10 × ?2000 = ?20,000
10 × ?500 = ?5,000
... (more balanced)
Mode 3: Minimize Large
- Goal: Use fewer large denominations
- Algorithm: Prefer smaller notes/coins
- Use Case: When large denominations are scarce
- Performance: O(n)
Mode 4: Minimize Small
- Goal: Use fewer small denominations
- Algorithm: Prefer larger notes/coins
- Use Case: When small denominations are scarce
- Performance: O(n)
4.1.4 Large Number Handling
Requirement: System MUST handle extremely large amounts without errors
| Amount | Description | Expected Behavior |
|---|---|---|
| 1,000 | One thousand | ? Standard calculation |
| 1,00,000 | One lakh | ? Standard calculation |
| 1,00,00,000 | One crore | ? Standard calculation |
| 1,00,00,00,000 | One hundred crore | ? Standard calculation |
| 10,00,00,00,000 | One thousand crore (10 billion) | ? Handle correctly |
| 1,00,00,00,00,000 | One lakh crore (1 trillion) | ? Handle correctly |
| 10,00,00,00,00,000 | Ten lakh crore (10 trillion) | ? Handle correctly |
Implementation
from decimal import Decimal, getcontext
# Set high precision
getcontext().prec = 50
def calculate_denominations(amount: str, currency: str):
# Convert to Decimal for arbitrary precision
amount_decimal = Decimal(amount)
# No overflow, no rounding errors
# Works for any size amount
Verification:
# Test file: packages/core-engine/test_engine.py
pytest test_engine.py::test_extreme_large_amounts
4.2 History Management
4.2.1 Full History
Feature: Store all calculations with complete details
Requirements:
- ? MUST store unlimited calculations (SQLite has no practical limit)
- ? MUST support pagination (default 50 per page)
- ? MUST support filtering by currency, date range
- ? MUST support sorting by date, amount
- ? MUST allow individual deletion
- ? MUST allow batch deletion (all, by filter)
Database Schema
CREATE TABLE calculations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
amount TEXT NOT NULL,
currency TEXT NOT NULL,
optimization_mode TEXT,
total_notes INTEGER,
total_coins INTEGER,
total_denominations INTEGER,
breakdown JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
calculation_time_ms REAL
);
API Endpoints
GET /api/v1/history?page=1&limit=50¤cy=INR&sort=date_desc
GET /api/v1/history/{id}
DELETE /api/v1/history/{id}
DELETE /api/v1/history/all
GET /api/v1/history/stats
4.2.2 Quick Access (Last 10)
Feature: Show last 10 calculations in sidebar for quick reference
Requirements:
- ? MUST show last 10 calculations chronologically
- ? MUST update in real-time when new calculation made
- ? MUST allow click to view full details
- ? MUST show: amount, currency, date, total denominations
UI Location: Left sidebar in History page
Implementation
const { data: quickAccess } = useQuery({
queryKey: ['history', 'quick-access'],
queryFn: () => api.getQuickAccessHistory(),
refetchInterval: false,
staleTime: Infinity
});
4.3 Bulk Upload System
4.3.1 Supported File Formats
?? CSV Files (.csv)
- Method: Direct parsing
- Max Size: 10 MB
- Encoding: UTF-8, UTF-16, ASCII
- Separators: Comma (,), Semicolon (;), Tab (\t)
?? PDF Files (.pdf)
- Method: PyMuPDF (text) + Tesseract OCR (scanned)
- Max Size: 50 MB
- Types: Text-based PDFs, Scanned PDFs, Mixed
- Pages: All pages processed
?? Word Documents (.docx, .doc)
- Method: python-docx
- Max Size: 10 MB
- Extraction: Paragraphs + Tables
- Format: DOCX (preferred), DOC (legacy support)
??? Image Files
- Formats: .jpg, .jpeg, .png, .tiff, .tif, .bmp, .gif, .webp
- Method: Tesseract OCR
- Max Size: 50 MB
- Resolution: Minimum 150 DPI recommended
4.3.2 CSV Format Specification
Standard Format
amount,currency,optimization_mode
1000,INR,greedy
250.50,USD,balanced
500,EUR,minimize_large
Minimal Format (with smart defaults)
amount,currency
1000,INR
250.50,USD
Ultra-Minimal (defaults: currency=INR, mode=greedy)
amount
1000
2500
5000
With Headers (optional)
Amount,Currency,Optimization Mode
1000,INR,greedy
250.50,USD,balanced
Flexible Separators
1000,INR,greedy
1000;INR;greedy
1000 INR greedy
4.3.3 Upload Process
- Drag & drop OR click to browse
- File validation (type, size, content)
- Preview of selected file
validateFile(file: File): string | null {
// Check extension
if (!supportedExtensions.includes(fileExtension)) {
return "Unsupported file format";
}
// Check size
if (file.size > maxSize) {
return "File too large";
}
// Check if empty
if (file.size === 0) {
return "File is empty";
}
return null; // Valid
}
[Client] ? FormData creation ? HTTP POST /api/calculations/bulk-upload
[Server] ? File type detection ? Route to processor
[Processor] ? Extract rows ? Parse each line
[Parser] ? Apply smart defaults ? Validate data
[Engine] ? Calculate each row ? Collect results
[Response] ? Return summary + details
- Summary cards (Total, Successful, Failed, Time)
- Detailed results table
- Export options (CSV, JSON)
- Error messages for failed rows
4.3.4 Smart Defaults
Default Currency: INR
- Applied when: No currency specified in row
- Configurable: Yes (in
ocr_processor.py) - Fallback Order: Specified ? System Default
Default Mode: greedy
- Applied when: No mode specified in row
- Configurable: Yes (in
ocr_processor.py) - Fallback Order: Specified ? System Default
Examples
Input: "5000"
Output: amount=5000, currency=INR, mode=greedy
Input: "5000 USD"
Output: amount=5000, currency=USD, mode=greedy
Input: "5000, EUR, balanced"
Output: amount=5000, currency=EUR, mode=balanced
4.4 OCR System
4.4.1 Tesseract Integration
Version: 5.3.3+ (latest stable)
Installation: Automatic via install_dependencies.ps1
Location: %LOCALAPPDATA%\CurrencyDistributor\Tesseract-OCR\
Languages: English (eng) by default
Configuration
# tesseract_config.py
TESSERACT_CONFIG = {
'lang': 'eng',
'config': '--psm 6 --oem 3',
'nice': 0
}
# PSM Modes:
# 0 = Orientation and script detection (OSD) only
# 1 = Automatic page segmentation with OSD
# 3 = Fully automatic page segmentation (default)
# 6 = Assume a single uniform block of text
# 11 = Sparse text. Find as much text as possible
# OEM Modes:
# 0 = Legacy engine only
# 1 = Neural nets LSTM engine only
# 2 = Legacy + LSTM engines
# 3 = Default, based on what is available
Usage
import pytesseract
from PIL import Image
image = Image.open('test_bulk_image.png')
text = pytesseract.image_to_string(image, config='--psm 6')
4.4.2 PDF Processing
Text-Based PDFs
import fitz # PyMuPDF
def _process_pdf_text(file_path: str) -> str:
doc = fitz.open(file_path)
text = ""
for page in doc:
text += page.get_text()
return text
Scanned PDFs
from pdf2image import convert_from_path
import pytesseract
def _process_scanned_pdf(file_path: str) -> str:
images = convert_from_path(file_path, dpi=300)
text = ""
for image in images:
text += pytesseract.image_to_string(image)
return text
Auto-Detection
def _process_pdf(file_path: str) -> str:
# Try text extraction first
text = _process_pdf_text(file_path)
# If mostly empty, use OCR
if len(text.strip()) < 50:
text = _process_scanned_pdf(file_path)
return text
4.4.3 Intelligent Text Parsing
Format Detection: The parser automatically detects and handles multiple formats:
Format 1: CSV-like
125.50, USD, greedy
250, EUR, balanced
Format 2: Pipe-separated
125.50 | USD | greedy
250 | EUR | balanced
Format 3: Tabular
Amount Currency Mode
125.50 USD greedy
250 EUR balanced
Format 4: Natural Language
Amount: 125.50 Currency: USD Mode: greedy
Total is 250 in EUR with balanced optimization
Format 5: Mixed
125.50 USD greedy
250 EUR
500
Parsing Logic
def _parse_line(self, line: str, line_number: int) -> Optional[Dict]:
# Extract amount (required)
amount = self._smart_extract_amount(line)
if not amount:
return None
# Extract currency (optional ? defaults to INR)
currency = self._smart_extract_currency(line)
if not currency:
currency = self.default_currency
# Extract mode (optional ? defaults to greedy)
mode = self._smart_extract_mode(line)
if not mode:
mode = self.default_mode
return {
'row_number': line_number,
'amount': amount,
'currency': currency,
'optimization_mode': mode
}
4.4.4 Currency Detection
Strategy 1: Currency Symbols
if '?' in text or 'rs.' in text.lower():
return 'INR'
if '$' in text:
return 'USD'
if '€' in text:
return 'EUR'
if '£' in text:
return 'GBP'
Strategy 2: Currency Names
currency_map = {
'rupee': 'INR', 'rupees': 'INR', 'rs': 'INR',
'dollar': 'USD', 'dollars': 'USD',
'euro': 'EUR', 'euros': 'EUR',
'pound': 'GBP', 'pounds': 'GBP'
}
Strategy 3: 3-Letter Codes
match = re.search(r'\b([A-Z]{3})\b', text.upper())
if match and match.group(1) not in ['THE', 'AND', 'FOR', ...]:
return match.group(1)
Strategy 4: Default Fallback
if not currency:
currency = 'INR' # System default
4.5 Export & Copy Features
4.5.1 CSV Export
Single Calculation
Amount,Currency,Optimization Mode,Total Notes,Total Coins,Total Denominations,Breakdown
50000,INR,greedy,25,0,1,"25 × ?2000 = ?50000"
Bulk Upload Results
Row Number,Status,Amount,Currency,Optimization Mode,Total Notes,Total Coins,Total Denominations,Error
1,success,1000,INR,greedy,2,0,2,
2,success,250.50,USD,balanced,8,3,11,
3,error,,,,,,,Invalid amount format
Implementation
handleExportCSV() {
const headers = ['Row', 'Status', 'Amount', 'Currency', ...];
const rows = results.map(row => [
row.row_number,
row.status,
row.amount,
...
]);
const csv = [headers, ...rows]
.map(row => row.map(cell => `"${cell}"`).join(','))
.join('\n');
downloadFile(csv, 'results.csv', 'text/csv');
}
4.5.2 JSON Export
Format
{
"export_date": "2025-11-25T10:30:00Z",
"total_rows": 4,
"successful": 3,
"failed": 1,
"results": [
{
"row_number": 1,
"status": "success",
"amount": "1000",
"currency": "INR",
"breakdown": [...]
}
]
}
4.5.3 Copy to Clipboard
Format
Bulk Upload Results
===================
Total Rows: 4
Successful: 3
Failed: 1
Processing Time: 0.12s
Detailed Results:
Row 1: ? 1000 INR ? 2 denominations
Row 2: ? 250.50 USD ? 11 denominations
Row 3: ? Invalid amount format
Implementation
async handleCopy() {
const text = formatResults(uploadResult);
await navigator.clipboard.writeText(text);
showNotification('Copied to clipboard!');
}
4.6 Multi-Language Support
4.6.1 Supported Languages
| Language | Code | Status | Translations | Completeness |
|---|---|---|---|---|
| English | en | ? Complete | 45+ keys | 100% |
| Hindi | hi | ? Complete | 45+ keys | 100% |
| Spanish | es | ? Complete | 45+ keys | 100% |
| French | fr | ? Complete | 45+ keys | 100% |
| German | de | ? Complete | 45+ keys | 100% |
4.6.2 Translation System
Backend:
- Location:
packages/local-backend/app/locales/ - Format: JSON files (
en.json,hi.json, etc.) - API:
/api/v1/translations/{language_code}
Frontend:
- Context:
LanguageContext.tsx - Hook:
useLanguage() - Function:
t('key', params)
Example
const { t, setLanguage } = useLanguage();
// Simple translation
<h1>{t('settings.title')}</h1>
// With parameters
<p>{t('history.showing', { count: 10 })}</p>
// Change language
setLanguage('hi');
4.6.3 Translation File Structure
{
"app": {
"title": "Currency Denomination Distributor",
"subtitle": "Calculate optimal denomination breakdowns"
},
"nav": {
"calculator": "Calculator",
"history": "History",
"settings": "Settings",
"bulkUpload": "Bulk Upload"
},
"calculator": {
"title": "Denomination Calculator",
"amount": "Amount",
"currency": "Currency",
"mode": "Optimization Mode",
"calculate": "Calculate"
}
}
4.7 Dark Mode
4.7.1 Theme System
Themes:
light- Light background, dark textdark- Dark background, light textsystem- Follow OS preference (future)
Implementation
// ThemeContext.tsx
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
useEffect(() => {
// Apply theme to document
document.documentElement.classList.toggle('dark', theme === 'dark');
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
Tailwind Configuration
// tailwind.config.js
module.exports = {
darkMode: 'class',
theme: {
extend: {
colors: {
dark: {
bg: '#1a1a1a',
surface: '#2d2d2d',
border: '#404040'
}
}
}
}
};
CSS Classes
/* Light mode */
.bg-white dark:bg-gray-800
.text-gray-900 dark:text-gray-100
.border-gray-200 dark:border-gray-700
/* Dark mode */
.dark .bg-white { background-color: #2d2d2d; }
.dark .text-gray-900 { color: #f3f4f6; }
4.7.2 Persistence
Backend
CREATE TABLE user_settings (
id INTEGER PRIMARY KEY,
theme TEXT DEFAULT 'light',
language TEXT DEFAULT 'en'
);
API
GET /api/v1/settings ? {theme: 'dark', language: 'en'}
PUT /api/v1/settings ? {theme: 'dark'}
Startup
// Load saved theme on app start
useEffect(() => {
api.getSettings().then(settings => {
setTheme(settings.theme);
});
}, []);
This section covers all 7 core features: Calculation Engine, History Management, Bulk Upload, OCR System, Export Features, Multi-Language Support, and Dark Mode.