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

  1. Add configuration to currencies.json
  2. Add FX rate to fx_rates_cache.json
  3. Update TypeScript types if needed
  4. 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&currency=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

Step 1: File Selection
  • Drag & drop OR click to browse
  • File validation (type, size, content)
  • Preview of selected file
Step 2: Validation
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
}
Step 3: Upload & Processing
[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
Step 4: Results Display
  • 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 text
  • dark - Dark background, light text
  • system - 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);
  });
}, []);
? Section Complete

This section covers all 7 core features: Calculation Engine, History Management, Bulk Upload, OCR System, Export Features, Multi-Language Support, and Dark Mode.