package org.ds;

import java.lang.reflect.Method;
import java.util.*;

/**
 * Simple test runner without JUnit dependencies.
 * This runner executes all test methods in ConsistentHashSimpleTest.
 */
public class TestRunner {
    private static int totalTests = 0;
    private static int passedTests = 0;
    private static int failedTests = 0;
    private static List<String> failures = new ArrayList<>();
    
    public static void main(String[] args) {
        System.out.println("╔════════════════════════════════════════════════════════════════╗");
        System.out.println("║       CONSISTENT HASHING - COMPREHENSIVE TEST SUITE            ║");
        System.out.println("╚════════════════════════════════════════════════════════════════╝\n");
        
        runAllTests();
        
        printSummary();
    }
    
    static void runAllTests() {
        // Basic Functionality Tests
        System.out.println("\n┌─ BASIC FUNCTIONALITY TESTS ───────────────────────────────────────┐");
        testEmptyRing();
        testAddSingleNode();
        testAddMultipleNodes();
        testGetNodeWithSingleNode();
        testKeyConsistency();
        
        // Node Removal Tests
        System.out.println("\n┌─ NODE REMOVAL TESTS ───────────────────────────────────────────────┐");
        testRemoveNode();
        testRoutingAfterRemoval();
        testRemoveAllNodes();
        
        // Load Distribution Tests
        System.out.println("\n┌─ LOAD DISTRIBUTION TESTS ─────────────────────────────────────────┐");
        testLoadDistribution();
        testVirtualNodesImpactDistribution();
        testRingSizeVsNodeCount();
        
        // Key Redistribution Tests
        System.out.println("\n┌─ KEY REDISTRIBUTION TESTS ────────────────────────────────────────┐");
        testKeyRedistributionOnNodeAddition();
        testKeyRedistributionOnNodeRemoval();
        testRapidNodeAdditions();
        testRapidNodeRemovals();
        
        // Edge Cases
        System.out.println("\n┌─ EDGE CASES & ROBUSTNESS TESTS ───────────────────────────────────┐");
        testDuplicateNodeAddition();
        testRemoveNonExistentNode();
        testEmptyStringKey();
        testVeryLongKey();
        testSpecialCharacterKeys();
        
        // Virtual Nodes Tests
        System.out.println("\n┌─ VIRTUAL NODES TESTS ────────────────────────────────────────────┐");
        testVirtualNodeCount();
        testVirtualNodesDistribution();
        
        // Hash Function Tests
        System.out.println("\n┌─ HASH FUNCTION TESTS ────────────────────────────────────────────┐");
        testMD5HashConsistency();
        testHashDiversity();
        
        // Complex Scenarios
        System.out.println("\n┌─ COMPLEX SCENARIO TESTS ────────────────────────────────────────┐");
        testCompleteLifecycle();
        testCacheClusterScenario();
        testLoadBalancingScenario();

        //added tests



        //multihash algo tests
        testMultiAlgorithmFunctionality();
        testMultiAlgorithmBalance();


        //weighted tests go here
        testWeightedDistribution();
    }
    
    // ============================================
    // Test Methods
    // ============================================
    
    static void testEmptyRing() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            assertTrue(hash.getNode("anyKey") == null, "Empty ring should return null");
            assertEquals(0, hash.getNodeCount(), "Node count should be 0");
            assertEquals(0, hash.getRingSize(), "Ring size should be 0");
            pass("testEmptyRing");
        } catch (Exception e) {
            fail("testEmptyRing", e);
        }
    }
    
    static void testAddSingleNode() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            assertEquals(1, hash.getNodeCount(), "Should have 1 node");
            assertEquals(150, hash.getRingSize(), "Ring size should be 150");
            assertTrue(hash.containsNode("node1"), "Should contain node1");
            pass("testAddSingleNode");
        } catch (Exception e) {
            fail("testAddSingleNode", e);
        }
    }
    
    static void testAddMultipleNodes() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            hash.addNode("node2");
            hash.addNode("node3");
            
            assertEquals(3, hash.getNodeCount(), "Should have 3 nodes");
            assertEquals(450, hash.getRingSize(), "Ring size should be 450");
            pass("testAddMultipleNodes");
        } catch (Exception e) {
            fail("testAddMultipleNodes", e);
        }
    }
    
    static void testGetNodeWithSingleNode() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            
            assertEquals("node1", hash.getNode("key1"), "Should route to node1");
            assertEquals("node1", hash.getNode("key2"), "Should route to node1");
            assertEquals("node1", hash.getNode("key3"), "Should route to node1");
            pass("testGetNodeWithSingleNode");
        } catch (Exception e) {
            fail("testGetNodeWithSingleNode", e);
        }
    }
    
    static void testKeyConsistency() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            hash.addNode("node2");
            
            String node1 = hash.getNode("testKey");
            String node2 = hash.getNode("testKey");
            String node3 = hash.getNode("testKey");
            
            assertEquals(node1, node2, "Same key should route to same node");
            assertEquals(node2, node3, "Same key should route to same node");
            pass("testKeyConsistency");
        } catch (Exception e) {
            fail("testKeyConsistency", e);
        }
    }
    
    static void testRemoveNode() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            hash.addNode("node2");
            
            assertEquals(2, hash.getNodeCount(), "Should have 2 nodes initially");
            
            hash.removeNode("node1");
            
            assertEquals(1, hash.getNodeCount(), "Should have 1 node after removal");
            assertEquals(150, hash.getRingSize(), "Ring size should be 150");
            assertFalse(hash.containsNode("node1"), "Should not contain node1");
            assertTrue(hash.containsNode("node2"), "Should still contain node2");
            pass("testRemoveNode");
        } catch (Exception e) {
            fail("testRemoveNode", e);
        }
    }
    
    static void testRoutingAfterRemoval() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            hash.addNode("node2");
            
            hash.removeNode("node1");
            
            assertEquals("node2", hash.getNode("anyKey"), "Should route to node2");
            pass("testRoutingAfterRemoval");
        } catch (Exception e) {
            fail("testRoutingAfterRemoval", e);
        }
    }
    
    static void testRemoveAllNodes() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            hash.addNode("node2");
            hash.addNode("node3");
            
            hash.removeNode("node1");
            hash.removeNode("node2");
            hash.removeNode("node3");
            
            assertEquals(0, hash.getNodeCount(), "Should have no nodes");
            assertTrue(hash.getNode("anyKey") == null, "Should return null for empty ring");
            pass("testRemoveAllNodes");
        } catch (Exception e) {
            fail("testRemoveAllNodes", e);
        }
    }
    
    static void testLoadDistribution() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            hash.addNode("node2");
            hash.addNode("node3");
            
            Map<String, Integer> distribution = hash.getDistribution(3000);
            
            assertTrue(distribution.get("node1") > 0, "node1 should get some keys");
            assertTrue(distribution.get("node2") > 0, "node2 should get some keys");
            assertTrue(distribution.get("node3") > 0, "node3 should get some keys");
            
            int total = distribution.values().stream().mapToInt(Integer::intValue).sum();
            assertEquals(3000, total, "Total should equal sample size");
            
            int idealCount = 3000 / 3;
            for (int count : distribution.values()) {
                assertTrue(count > idealCount * 0.6, "Count should not be too low");
                assertTrue(count < idealCount * 1.4, "Count should not be too high");
            }
            
            pass("testLoadDistribution");
        } catch (Exception e) {
            fail("testLoadDistribution", e);
        }
    }
    
    static void testVirtualNodesImpactDistribution() {
        try {
            ConsistentHash<String> hash10 = new ConsistentHash<>(10);
            ConsistentHash<String> hash500 = new ConsistentHash<>(500);
            
            for (ConsistentHash<String> hash : Arrays.asList(hash10, hash500)) {
                hash.addNode("node1");
                hash.addNode("node2");
                hash.addNode("node3");
            }
            
            Map<String, Integer> dist10 = hash10.getDistribution(3000);
            Map<String, Integer> dist500 = hash500.getDistribution(3000);
            
            double stdDev10 = calculateStdDev(dist10.values());
            double stdDev500 = calculateStdDev(dist500.values());
            
            assertTrue(stdDev500 <= stdDev10, "More virtual nodes should improve distribution");
            pass("testVirtualNodesImpactDistribution");
        } catch (Exception e) {
            fail("testVirtualNodesImpactDistribution", e);
        }
    }
    
    static void testRingSizeVsNodeCount() {
        try {
            int virtualNodes = 150;
            ConsistentHash<String> hash = new ConsistentHash<>(virtualNodes);
            
            for (int i = 1; i <= 5; i++) {
                hash.addNode("node" + i);
                assertEquals(i * virtualNodes, hash.getRingSize(), 
                    "Ring size should be nodes * virtualNodes");
            }
            
            pass("testRingSizeVsNodeCount");
        } catch (Exception e) {
            fail("testRingSizeVsNodeCount", e);
        }
    }
    
    static void testKeyRedistributionOnNodeAddition() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            hash.addNode("node2");
            
            int sampleSize = 5000;
            Map<String, String> before = captureAssignments(hash, sampleSize);
            
            hash.addNode("node3");
            
            Map<String, String> after = captureAssignments(hash, sampleSize);
            
            int movedKeys = countMovedKeys(before, after);
            double movedPercentage = (movedKeys * 100.0) / sampleSize;
            
            assertTrue(movedPercentage < 50, "Should not move too many keys");
            assertTrue(movedPercentage > 20, "Should move some keys");
            pass("testKeyRedistributionOnNodeAddition");
        } catch (Exception e) {
            fail("testKeyRedistributionOnNodeAddition", e);
        }
    }
    
    static void testKeyRedistributionOnNodeRemoval() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            hash.addNode("node2");
            hash.addNode("node3");
            
            int sampleSize = 5000;
            Map<String, String> before = captureAssignments(hash, sampleSize);
            
            hash.removeNode("node3");
            
            Map<String, String> after = captureAssignments(hash, sampleSize);
            
            int movedKeys = countMovedKeys(before, after);
            double movedPercentage = (movedKeys * 100.0) / sampleSize;
            
            assertTrue(movedPercentage < 50, "Should not move too many keys");
            pass("testKeyRedistributionOnNodeRemoval");
        } catch (Exception e) {
            fail("testKeyRedistributionOnNodeRemoval", e);
        }
    }
    
    static void testRapidNodeAdditions() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            for (int i = 1; i <= 10; i++) {
                hash.addNode("node" + i);
            }
            
            assertEquals(10, hash.getNodeCount(), "Should have 10 nodes");
            assertEquals(1500, hash.getRingSize(), "Ring size should be 1500");
            
            for (int i = 0; i < 100; i++) {
                assertNotNull(hash.getNode("key:" + i), "All keys should be routable");
            }
            
            pass("testRapidNodeAdditions");
        } catch (Exception e) {
            fail("testRapidNodeAdditions", e);
        }
    }
    
    static void testRapidNodeRemovals() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            for (int i = 1; i <= 10; i++) {
                hash.addNode("node" + i);
            }
            
            for (int i = 10; i > 0; i--) {
                hash.removeNode("node" + i);
            }
            
            assertEquals(0, hash.getNodeCount(), "Should have no nodes");
            assertTrue(hash.getNode("anyKey") == null, "Should return null");
            pass("testRapidNodeRemovals");
        } catch (Exception e) {
            fail("testRapidNodeRemovals", e);
        }
    }
    
    static void testDuplicateNodeAddition() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            int sizeAfter1 = hash.getRingSize();
            
            hash.addNode("node1");
            int sizeAfter2 = hash.getRingSize();
            
            assertEquals(sizeAfter1, sizeAfter2, "Ring size should not change");
            pass("testDuplicateNodeAddition");
        } catch (Exception e) {
            fail("testDuplicateNodeAddition", e);
        }
    }
    
    static void testRemoveNonExistentNode() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            int sizeBefore = hash.getRingSize();
            
            hash.removeNode("node2");
            
            assertEquals(sizeBefore, hash.getRingSize(), "Ring size should not change");
            assertEquals(1, hash.getNodeCount(), "Should still have 1 node");
            pass("testRemoveNonExistentNode");
        } catch (Exception e) {
            fail("testRemoveNonExistentNode", e);
        }
    }
    
    static void testEmptyStringKey() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            assertNotNull(hash.getNode(""), "Should handle empty string");
            pass("testEmptyStringKey");
        } catch (Exception e) {
            fail("testEmptyStringKey", e);
        }
    }
    
    static void testVeryLongKey() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            String longKey = "k".repeat(10000);
            assertNotNull(hash.getNode(longKey), "Should handle long keys");
            pass("testVeryLongKey");
        } catch (Exception e) {
            fail("testVeryLongKey", e);
        }
    }
    
    static void testSpecialCharacterKeys() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("node1");
            
            String[] specialKeys = {
                "key-with-dashes",
                "key_with_underscores",
                "key.with.dots",
                "key@with#special$chars"
            };
            
            for (String key : specialKeys) {
                assertNotNull(hash.getNode(key), "Should handle special characters in key");
            }
            
            pass("testSpecialCharacterKeys");
        } catch (Exception e) {
            fail("testSpecialCharacterKeys", e);
        }
    }
    
    static void testVirtualNodeCount() {
        try {
            int[] virtualNodeCounts = {1, 10, 50, 150, 500};
            
            for (int count : virtualNodeCounts) {
                ConsistentHash<String> hash = new ConsistentHash<>(count);
                hash.addNode("node1");
                assertEquals(count, hash.getRingSize(), "Ring size should match virtual nodes");
            }
            
            pass("testVirtualNodeCount");
        } catch (Exception e) {
            fail("testVirtualNodeCount", e);
        }
    }
    
    static void testVirtualNodesDistribution() {
        try {
            ConsistentHash<String> hash1 = new ConsistentHash<>(1);
            ConsistentHash<String> hash150 = new ConsistentHash<>(150);
            
            hash1.addNode("node1");
            hash1.addNode("node2");
            hash150.addNode("node1");
            hash150.addNode("node2");
            
            Map<String, Integer> dist1 = hash1.getDistribution(1000);
            Map<String, Integer> dist150 = hash150.getDistribution(1000);
            
            int diff1 = Math.abs(dist1.get("node1") - dist1.get("node2"));
            int diff150 = Math.abs(dist150.get("node1") - dist150.get("node2"));
            
            assertTrue(diff150 < diff1, "More virtual nodes should improve balance");
            pass("testVirtualNodesDistribution");
        } catch (Exception e) {
            fail("testVirtualNodesDistribution", e);
        }
    }
    
    static void testMD5HashConsistency() {
        try {
            ConsistentHash.HashFunction md5 = new ConsistentHash.MD5HashFunction();
            
            long hash1 = md5.hash("testKey");
            long hash2 = md5.hash("testKey");
            
            assertEquals(hash1, hash2, "Same key should have same hash");
            pass("testMD5HashConsistency");
        } catch (Exception e) {
            fail("testMD5HashConsistency", e);
        }
    }
    
    static void testHashDiversity() {
        try {
            ConsistentHash.HashFunction md5 = new ConsistentHash.MD5HashFunction();
            
            long hash1 = md5.hash("key1");
            long hash2 = md5.hash("key2");
            long hash3 = md5.hash("key3");
            
            assertNotEquals(hash1, hash2, "Different keys should have different hashes");
            assertNotEquals(hash2, hash3, "Different keys should have different hashes");
            assertNotEquals(hash1, hash3, "Different keys should have different hashes");
            pass("testHashDiversity");
        } catch (Exception e) {
            fail("testHashDiversity", e);
        }
    }
    
    static void testCompleteLifecycle() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            
            assertEquals(0, hash.getNodeCount(), "Should start empty");
            
            for (int i = 1; i <= 5; i++) {
                hash.addNode("server" + i);
            }
            assertEquals(5, hash.getNodeCount(), "Should have 5 nodes");
            
            Map<String, Integer> dist = hash.getDistribution(1000);
            assertTrue(dist.values().stream().allMatch(v -> v > 0), "All nodes should get keys");
            
            hash.removeNode("server1");
            hash.removeNode("server3");
            assertEquals(3, hash.getNodeCount(), "Should have 3 nodes");
            
            for (int i = 0; i < 100; i++) {
                assertNotNull(hash.getNode("key:" + i), "Keys should still be routable");
            }
            
            hash.clear();
            assertEquals(0, hash.getNodeCount(), "Should be empty after clear");
            assertTrue(hash.getNode("anyKey") == null, "Should return null");
            pass("testCompleteLifecycle");
        } catch (Exception e) {
            fail("testCompleteLifecycle", e);
        }
    }
    
    static void testCacheClusterScenario() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("cache1");
            hash.addNode("cache2");
            hash.addNode("cache3");
            
            Map<String, String> assignments = new HashMap<>();
            for (int i = 0; i < 100; i++) {
                String key = "data:" + i;
                assignments.put(key, hash.getNode(key));
            }
            
            hash.removeNode("cache2");
            
            int reassignedCount = 0;
            for (Map.Entry<String, String> entry : assignments.entrySet()) {
                String newNode = hash.getNode(entry.getKey());
                if (!newNode.equals(entry.getValue())) {
                    reassignedCount++;
                }
            }
            
            assertTrue(reassignedCount > 0, "Some data should be reassigned");
            assertTrue(reassignedCount < 100, "Not all data should be reassigned");
            
            hash.addNode("cache2");
            Map<String, Integer> finalDist = hash.getDistribution(1000);
            assertTrue(finalDist.get("cache2") > 0, "Cache2 should serve requests");
            pass("testCacheClusterScenario");
        } catch (Exception e) {
            fail("testCacheClusterScenario", e);
        }
    }
    
    static void testLoadBalancingScenario() {
        try {
            ConsistentHash<String> hash = new ConsistentHash<>(150);
            hash.addNode("web-server-1");
            hash.addNode("web-server-2");
            hash.addNode("web-server-3");
            
            Map<String, Integer> load = hash.getDistribution(10000);
            
            load.forEach((server, count) -> {
                assertTrue(count > 0, "Server should handle requests");
                double percentage = (count * 100.0) / 10000;
                assertTrue(percentage > 20, "Server should not have too few requests");
            });
            
            pass("testLoadBalancingScenario");
        } catch (Exception e) {
            fail("testLoadBalancingScenario", e);
        }
    }

    // ============================================
    // MULTI-ALGORITHM EXTENSION TESTS
    // ============================================

    static void testMultiAlgorithmFunctionality() {
        try {
            System.out.println("  Testing Multiple Hash Function extension...");
            MultiAlgorithmConsistentHash<String> multi = new MultiAlgorithmConsistentHash<>(30);
            multi.addNode("server-1");
            
            assertEquals(30, multi.getRingSize(), "Should have 30 points on the ring");
            assertNotNull(multi.getNode("test-key"), "Should successfully route a key");
            
            multi.removeNode("server-1");
            assertEquals(0, multi.getRingSize(), "Ring should be empty after removal");
            pass("testMultiAlgorithmFunctionality");
        } catch (Exception e) {
            fail("testMultiAlgorithmFunctionality", e);
        }
    }

    static void testMultiAlgorithmBalance() {
        try {
            System.out.println("  Testing load balance with multiple algorithms...");
            MultiAlgorithmConsistentHash<String> multi = new MultiAlgorithmConsistentHash<>(300);
            multi.addNode("A");
            multi.addNode("B");
            multi.addNode("C");

            Map<String, Integer> dist = multi.getDistribution(10000);
            
            // Check that all nodes got roughly 33% (statistically even)
            for (String node : Arrays.asList("A", "B", "C")) {
                int count = dist.get(node);
                assertTrue(count > 2500 && count < 4000, 
                    "Node " + node + " distribution (" + count + ") is uneven");
            }
            pass("testMultiAlgorithmBalance");
        } catch (Exception e) {
            fail("testMultiAlgorithmBalance", e);
        }
    }


    // ============================================
    // WEIGHTED DISTRIBUTION TESTS
    // ============================================

    static void testWeightedDistribution() {
        try {
            System.out.println("\n┌─ WEIGHTED CAPACITY TESTS ────────────────────────────────────────┐");
            // Base VN is 50
            WeightedConsistentHash<String> weightedHash = new WeightedConsistentHash<>(50);
            
            System.out.println("Adding 'Small-Server' (Weight 1) and 'Giant-Server' (Weight 10)...");
            weightedHash.addNode("Small-Server", 1);  // Gets 50 virtual nodes
            weightedHash.addNode("Giant-Server", 10); // Gets 500 virtual nodes
            
            // Run distribution check
            int sampleSize = 10000;
            Map<String, Integer> dist = weightedHash.getDistribution(sampleSize);
            
            int smallCount = dist.get("Small-Server");
            int giantCount = dist.get("Giant-Server");
            
            double ratio = (double) giantCount / smallCount;
            
            System.out.printf("  Small Server keys: %d\n", smallCount);
            System.out.printf("  Giant Server keys: %d\n", giantCount);
            System.out.printf("  Measured Ratio:    %.2fx (Target: ~10x)\n", ratio);
            
            // Verify Giant server took significantly more (at least 7x to account for hash variance)
            assertTrue(ratio > 7.0, "Giant server should have significantly more load");
            pass("testWeightedDistribution");
        } catch (Exception e) {
            fail("testWeightedDistribution", e);
        }
    }
    
    // ============================================
    // Assertion Methods
    // ============================================
    
    static void assertTrue(boolean condition, String message) throws AssertionError {
        if (!condition) throw new AssertionError(message);
    }
    
    static void assertFalse(boolean condition, String message) throws AssertionError {
        if (condition) throw new AssertionError(message);
    }
    
    static void assertEquals(Object expected, Object actual, String message) throws AssertionError {
        if (!expected.equals(actual)) {
            throw new AssertionError(message + " | Expected: " + expected + ", Got: " + actual);
        }
    }
    
    static void assertNotNull(Object value, String message) throws AssertionError {
        if (value == null) throw new AssertionError(message);
    }
    
    static void assertNotEquals(Object expected, Object actual, String message) throws AssertionError {
        if (expected.equals(actual)) {
            throw new AssertionError(message);
        }
    }
    
    // ============================================
    // Helper Methods
    // ============================================
    
    static void pass(String testName) {
        totalTests++;
        passedTests++;
        System.out.println("  ✓ " + testName);
    }
    
    static void fail(String testName, Exception e) {
        totalTests++;
        failedTests++;
        System.out.println("  ✗ " + testName + " - " + e.getMessage());
        failures.add(testName + ": " + e.getMessage());
    }
    
    static Map<String, String> captureAssignments(ConsistentHash<String> hash, int count) {
        Map<String, String> assignments = new HashMap<>();
        for (int i = 0; i < count; i++) {
            String key = "key:" + i;
            assignments.put(key, hash.getNode(key));
        }
        return assignments;
    }
    
    static int countMovedKeys(Map<String, String> before, Map<String, String> after) {
        int count = 0;
        for (String key : before.keySet()) {
            if (!before.get(key).equals(after.get(key))) {
                count++;
            }
        }
        return count;
    }
    
    static double calculateStdDev(java.util.Collection<Integer> values) {
        if (values.isEmpty()) return 0;
        double mean = values.stream().mapToDouble(Integer::doubleValue).average().orElse(0);
        double variance = values.stream()
            .mapToDouble(v -> Math.pow(v - mean, 2))
            .average()
            .orElse(0);
        return Math.sqrt(variance);
    }
    
    static void printSummary() {
        System.out.println("\n╔════════════════════════════════════════════════════════════════╗");
        System.out.println("║                        TEST SUMMARY                             ║");
        System.out.println("╚════════════════════════════════════════════════════════════════╝");
        System.out.println("Total Tests:   " + totalTests);
        System.out.println("Passed:        " + passedTests + " ✓");
        System.out.println("Failed:        " + failedTests + " ✗");
        
        if (failedTests > 0) {
            System.out.println("\nFailed Tests:");
            for (String failure : failures) {
                System.out.println("  - " + failure);
            }
        }
        
        double percentage = (passedTests * 100.0) / totalTests;
        System.out.printf("\nSuccess Rate:  %.1f%%\n", percentage);
        
        if (failedTests == 0) {
            System.out.println("\n🎉 ALL TESTS PASSED!\n");
        } else {
            System.out.println("\n⚠ SOME TESTS FAILED\n");
        }
    }
}

