Skip to main content

Debugging & Stress Testing

The Cost of Debugging in CP

Every minute spent debugging is a minute not solving the next problem. Learn to debug fast.
┌─────────────────────────────────────────────────────┐
│         TIME COST OF BUGS IN CONTEST                │
├─────────────────────────────────────────────────────┤
│  Wrong Answer (WA)                                  │
│    - Finding bug: 3-10 minutes                      │
│    - Penalty: varies by scoring system (see below)  │
│    - Total: significant time lost                   │
│                                                     │
│  Time Limit Exceeded (TLE)                          │
│    - Rethinking approach: 5-15 minutes              │
│    - Rewriting: 5-10 minutes                        │
│    - Total: 10-25 minutes lost                      │
└─────────────────────────────────────────────────────┘
Contest Scoring Systems on Codeforces:ICPC Rules (most Div 2/3/4 contests):
  • Each wrong submission adds +10 minute penalty
  • Final score = time of AC + (10 × wrong attempts)
  • Earlier solve = better rank
IOI Rules (Educational rounds, some special contests):
  • Partial scoring based on test cases passed
  • No time penalty for wrong submissions
  • Score = points earned (not time-based)
  • Maximum points per problem (e.g., 100, 250, 500, etc.)
Codeforces Scoring (standard Div 1/2 rounds):
  • Points decrease over time (max points at start)
  • Each wrong submission costs -50 points (before AC)
  • No penalty after AC

Prevention > Debugging

Before You Code

1

Understand the Problem Completely

Re-read after you think you understand. Check examples manually.
2

Plan Your Approach

Think through edge cases before coding.
3

Choose Data Types Carefully

Will values overflow int? Use long long proactively.
4

Write Clean Code

Use meaningful variable names. Keep functions small.

Common Bug Prevention

// Use these by default
#define int long long  // Prevent overflow

// Check array bounds explicitly
if (i >= 0 && i < n && j >= 0 && j < m) {
    // Safe to access grid[i][j]
}

// Initialize variables
int sum = 0;  // Not just: int sum;

// Use const for array sizes
const int N = 2e5 + 5;
int arr[N];

Systematic Debugging Process

Step 1: Read the Verdict

VerdictFirst Action
WACheck edge cases, trace smallest example
TLECheck complexity, look for infinite loops
RECheck bounds, division by zero
MLECheck array sizes, recursive depth

Step 2: Test Edge Cases

Always test these inputs:
// Minimum cases
n = 1
n = 0 (if allowed)
empty input

// Boundary cases
All same values
All different values
Maximum constraints
Minimum values (0, 1, -1)

// Special patterns
Sorted ascending
Sorted descending
Alternating pattern

Step 3: Trace Through by Hand

For small inputs, trace your code step by step:
// For input: n=3, arr=[1, 2, 3]
// Trace each line:
// i=0: sum=0+1=1
// i=1: sum=1+2=3
// i=2: sum=3+3=6
// Output: 6 ✓

Step 4: Add Debug Output

#ifdef LOCAL
#define debug(x) cerr << #x << " = " << x << endl
#else
#define debug(x)
#endif

// Usage
int ans = calculate(n);
debug(ans);  // Only prints when compiled with -DLOCAL

Common Bug Patterns

Integer Overflow

// BUG: Overflow in multiplication
int a = 100000, b = 100000;
int product = a * b;  // Overflow!

// FIX: Use long long
long long product = (long long)a * b;

// Or use #define
#define int long long

Off-by-One Errors

// BUG: Accessing arr[n] (out of bounds)
for (int i = 0; i <= n; i++) {
    arr[i] = 0;  // When i=n, this is out of bounds!
}

// FIX: Use < instead of <=
for (int i = 0; i < n; i++) {
    arr[i] = 0;
}

Uninitialized Variables

// BUG: Using uninitialized variable
int count;
for (int x : arr) {
    if (x > 0) count++;  // count is garbage!
}

// FIX: Initialize
int count = 0;

Array Index Errors

// BUG: 1-indexed vs 0-indexed confusion
for (int i = 1; i <= n; i++) {
    cin >> arr[i];  // Works if arr has size n+1
}
// Later...
for (int i = 0; i < n; i++) {
    // arr[i] has garbage for i=0!
}

// FIX: Be consistent, prefer 0-indexed

Multiple Test Case Issues

// BUG: Not resetting between test cases
int globalCounter = 0;
vector<int> seen;

void solve() {
    // globalCounter and seen retain old values!
}

// FIX: Reset at start of each test case
void solve() {
    int counter = 0;  // Local variable
    vector<int> seen; // Fresh vector
    // Or explicitly clear: seen.clear();
}

Floating Point Comparison

// BUG: Direct floating point comparison
double a = 0.1 + 0.2;
if (a == 0.3) {  // This is FALSE!
    // ...
}

// FIX: Use epsilon comparison
const double EPS = 1e-9;
if (abs(a - 0.3) < EPS) {
    // ...
}

Debug Macros Template

Add this to your template:
#ifdef LOCAL
    #define debug(x) cerr << "[" << #x << " = " << (x) << "]" << endl
    #define debugv(v) { cerr << "[" << #v << ": "; for(auto& x : v) cerr << x << " "; cerr << "]" << endl; }
    #define debugm(m) { cerr << "[" << #m << "]:" << endl; for(auto& row : m) { for(auto& x : row) cerr << x << " "; cerr << endl; } }
#else
    #define debug(x)
    #define debugv(v)
    #define debugm(m)
#endif

// Usage:
int n = 5;
debug(n);  // Prints: [n = 5]

vector<int> arr = {1, 2, 3};
debugv(arr);  // Prints: [arr: 1 2 3]

vector<vector<int>> grid = {{1,2},{3,4}};
debugm(grid);  // Prints 2D grid
Compile with -DLOCAL to enable debug output:
g++ -DLOCAL -o solution solution.cpp

Stress Testing

What is Stress Testing?

Stress testing = Running your solution against a brute force on random inputs until they disagree.
┌─────────────────────────────────────────────────────┐
│              STRESS TEST FLOW                       │
├─────────────────────────────────────────────────────┤
│  1. Write brute force solution (slow but correct)   │
│  2. Write optimized solution (fast but buggy?)      │
│  3. Generate random test case                        │
│  4. Run both solutions                               │
│  5. Compare outputs                                  │
│  6. If different → found the bug!                   │
│  7. Repeat until confident                           │
└─────────────────────────────────────────────────────┘

Complete Stress Test Setup

File 1: solution.cpp (your optimized code)
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> arr(n);
    for (int& x : arr) cin >> x;
    
    // Your optimized solution
    int ans = 0;
    // ...
    
    cout << ans << endl;
    return 0;
}
File 2: brute.cpp (simple correct solution)
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> arr(n);
    for (int& x : arr) cin >> x;
    
    // Brute force O(n²) or O(n³) - slow but correct
    int ans = 0;
    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            // Check every possibility
        }
    }
    
    cout << ans << endl;
    return 0;
}
File 3: gen.cpp (random test generator)
#include <bits/stdc++.h>
using namespace std;

int main(int argc, char* argv[]) {
    mt19937 rng(atoi(argv[1]));  // Seed from command line
    
    int n = rng() % 10 + 1;  // n from 1 to 10 (small for stress)
    cout << n << endl;
    
    for (int i = 0; i < n; i++) {
        cout << (rng() % 100 + 1);  // values from 1 to 100
        if (i < n-1) cout << " ";
    }
    cout << endl;
    
    return 0;
}
File 4: stress.bat (Windows)
@echo off
g++ -O2 solution.cpp -o solution.exe
g++ -O2 brute.cpp -o brute.exe
g++ -O2 gen.cpp -o gen.exe

for /L %%i in (1,1,1000) do (
    gen.exe %%i > input.txt
    solution.exe < input.txt > output1.txt
    brute.exe < input.txt > output2.txt
    fc output1.txt output2.txt > nul
    if errorlevel 1 (
        echo Test %%i FAILED!
        type input.txt
        echo Solution output:
        type output1.txt
        echo Brute output:
        type output2.txt
        exit /b 1
    )
    echo Test %%i passed
)
echo All tests passed!
File 4: stress.sh (Linux/Mac)
#!/bin/bash
g++ -O2 solution.cpp -o solution
g++ -O2 brute.cpp -o brute
g++ -O2 gen.cpp -o gen

for i in $(seq 1 1000); do
    ./gen $i > input.txt
    ./solution < input.txt > output1.txt
    ./brute < input.txt > output2.txt
    if ! diff -q output1.txt output2.txt > /dev/null; then
        echo "Test $i FAILED!"
        echo "Input:"
        cat input.txt
        echo "Solution output:"
        cat output1.txt
        echo "Brute output:"
        cat output2.txt
        exit 1
    fi
    echo "Test $i passed"
done
echo "All tests passed!"

Quick Debug Checklist

When you get WA, check in this order:
1

Did I read the problem correctly?

Re-read problem statement. Check constraints again.
2

Integer overflow?

Use long long for any multiplication or large sums.
3

Edge cases?

Test n=1, n=0, all same, all different.
4

Off-by-one?

Check loop bounds, array indexing.
5

Uninitialized variables?

Check all variables are initialized.
6

Multiple test cases?

Are you resetting everything between cases?
7

Output format?

Spaces, newlines, YES vs Yes vs yes?

Debugging Different Verdicts

Wrong Answer (WA)

  1. Test all examples manually
  2. Create small custom test
  3. Check edge cases
  4. Add debug output
  5. Stress test if needed

Time Limit Exceeded (TLE)

  1. Check complexity analysis
  2. Look for accidental O(n²) operations
  3. Check for infinite loops
  4. Use faster I/O if not already
  5. Consider algorithm change
// Common hidden O(n²):
for (int i = 0; i < n; i++) {
    string s = str.substr(i);  // O(n) each time!
}

// Fix: Avoid creating substrings

Runtime Error (RE)

  1. Check array bounds
  2. Check division by zero
  3. Check stack overflow (deep recursion)
  4. Check null pointer / empty container
// Common RE causes:
arr[n];     // Out of bounds (0-indexed)
x / y;      // y might be 0
vec.back(); // vec might be empty
*ptr;       // ptr might be null

Memory Limit Exceeded (MLE)

  1. Check array sizes
  2. Avoid unnecessary copies
  3. Use iterative instead of recursive
  4. Use memory-efficient data structures
// MLE causes:
int arr[1000000000];  // Too big!
vector<vector<int>> adj(n, vector<int>(n));  // O(n²) memory

// Fixes:
vector<int> arr(n);  // Dynamic allocation
vector<vector<int>> adj(n);  // Adjacency list

Pro Debugging Tips

When stuck on one test case:
// Find which test case fails
int caseNum = 0;
while (t--) {
    caseNum++;
    // If answer should be X for case 5:
    if (caseNum == 5) {
        cerr << "Debug case 5" << endl;
        debug(n);
        debugv(arr);
    }
    solve();
}

Binary Search on Test Cases

If failing on test case 50 out of 100:
// Skip first 49 cases
int skip = 49;
while (t--) {
    if (skip > 0) {
        skip--;
        // Read input but don't process
        int n; cin >> n;
        // ... discard ...
        continue;
    }
    solve();  // Only solve case 50
}

Assert for Assumptions

#include <cassert>

void solve(int n) {
    assert(n >= 1 && n <= 100000);  // Crash if invalid
    // ...
}

Key Takeaways

Prevention First

Use safe defaults: long long, initialize variables, check bounds.

Systematic Process

Follow a checklist. Don’t randomly try fixes.

Debug Macros

Have debug macros ready in your template.

Stress Test

When stuck, stress test against brute force.

Next Steps

Environment Setup

Set up VS Code with tasks for stress testing.