From 7e789a5e4ed0e496841a140fc44b7ea73fd34607 Mon Sep 17 00:00:00 2001 From: ginks21z Date: Wed, 15 Jul 2026 19:57:12 +0430 Subject: [PATCH] idk --- src/main/java/ChunkStatus.java | 5 + src/main/java/ConfigReader.java | 12 ++- src/main/java/DownloadConfig.java | 14 ++- src/main/java/DownloadWorker.java | 58 ++++++++--- src/main/java/Main.java | 150 +++++++++++++++++++---------- src/main/java/ProgressMonitor.java | 57 ++++++++--- 6 files changed, 219 insertions(+), 77 deletions(-) diff --git a/src/main/java/ChunkStatus.java b/src/main/java/ChunkStatus.java index 3e902dc..b020e84 100644 --- a/src/main/java/ChunkStatus.java +++ b/src/main/java/ChunkStatus.java @@ -105,4 +105,9 @@ public class ChunkStatus { completed ? " [Completed]" : "" ); } + + public void setStartTime(long startTime) { + this.startTimeMs = startTime; + } } + diff --git a/src/main/java/ConfigReader.java b/src/main/java/ConfigReader.java index 5754060..1eae77d 100644 --- a/src/main/java/ConfigReader.java +++ b/src/main/java/ConfigReader.java @@ -20,6 +20,8 @@ public class ConfigReader { int maxStepDelayMs = 0; double minStepDownloadMB = 0; double maxStepDownloadMB = 0; + int minDelayMs = 0; + int maxDelayMs = 0; try (BufferedReader reader = new BufferedReader( new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { @@ -62,6 +64,12 @@ public class ConfigReader { case "maxStepDownloadMB": maxStepDownloadMB = Double.parseDouble(value); break; + case "minDelayMs": + minDelayMs = Integer.parseInt(value); + break; + case "maxDelayMs": + maxDelayMs = Integer.parseInt(value); + break; default: // Ignore unknown keys to keep parsing simple break; @@ -79,7 +87,9 @@ public class ConfigReader { minStepDelayMs, maxStepDelayMs, minStepDownloadMB, - maxStepDownloadMB + maxStepDownloadMB, + minDelayMs, + maxDelayMs ); } } diff --git a/src/main/java/DownloadConfig.java b/src/main/java/DownloadConfig.java index dc1ba3a..e765d75 100644 --- a/src/main/java/DownloadConfig.java +++ b/src/main/java/DownloadConfig.java @@ -10,13 +10,15 @@ public class DownloadConfig { private final int maxStepDelayMs; private final double minStepDownloadMB; private final double maxStepDownloadMB; + private final int maxDelayMs; + private final int minDelayMs; /** * Constructs a new DownloadConfig with specified simulation parameters. */ public DownloadConfig(String fileName, int totalSizeMB, int chunkCount, int minStepDelayMs, int maxStepDelayMs, - double minStepDownloadMB, double maxStepDownloadMB) { + double minStepDownloadMB, double maxStepDownloadMB, int maxDelayMs, int minDelayMs) { this.fileName = fileName; this.totalSizeMB = totalSizeMB; this.chunkCount = chunkCount; @@ -24,6 +26,8 @@ public class DownloadConfig { this.maxStepDelayMs = maxStepDelayMs; this.minStepDownloadMB = minStepDownloadMB; this.maxStepDownloadMB = maxStepDownloadMB; + this.maxDelayMs = maxDelayMs; + this.minDelayMs = minDelayMs; } // Getters @@ -67,4 +71,12 @@ public class DownloadConfig { ", maxStepDownloadMB=" + maxStepDownloadMB + '}'; } + + public int getMinDelayMs() { + return minDelayMs; + } + + public int getMaxDelayMs() { + return maxDelayMs; + } } diff --git a/src/main/java/DownloadWorker.java b/src/main/java/DownloadWorker.java index 546de2d..36da4a9 100644 --- a/src/main/java/DownloadWorker.java +++ b/src/main/java/DownloadWorker.java @@ -21,23 +21,57 @@ public class DownloadWorker implements Runnable { @Override public void run() { - // TODO: Record the chunk start time in chunkStatus. + long startTime = System.currentTimeMillis(); + chunkStatus.setStartTime(startTime); + double downloaded = 0.0; - // TODO: Print a message that this chunk has started downloading. + System.out.println("Chunk \"" + chunkStatus.getChunkId() + "\" started downloading."); while (downloaded < chunkStatus.getChunkSizeMB()) { - // TODO: Generate a random sleep delay between min and max delay. - // TODO: Sleep for that delay. - // TODO: Generate a random download amount for this step. - // TODO: Increase downloaded, but do not go beyond chunk size. - // TODO: Save the updated downloaded value into chunkStatus. - // TODO: Optionally print step-by-step progress. + + try { + //Generate a random sleep delay between min and max delay. + int minDelay = config.getMinDelayMs(); + int maxDelay = config.getMaxDelayMs(); + int randomDelay = random.nextInt((maxDelay - minDelay) + 1) + minDelay; + + //Sleep for that delay. + Thread.sleep(randomDelay); + + //Generate a random download amount for this step. + double minStep = config.getMinStepDelayMs(); + double maxStep = config.getMaxStepDelayMs(); + double randomStep = (maxStep + minStep)*random.nextDouble() + minStep; + + //Increase downloaded, but do not go beyond chunk size. + downloaded += randomStep; + if (downloaded > chunkStatus.getChunkSizeMB()) { + downloaded = chunkStatus.getChunkSizeMB(); + } + + //Save the updated downloaded value into chunkStatus. + chunkStatus.setDownloadedMB(downloaded); + + //print step-by-step progress. + System.out.printf("Chunk #%d: Downloaded %.2f / %.2f MB%n", + chunkStatus.getChunkId(), downloaded, chunkStatus.getChunkSizeMB()); + + } catch (InterruptedException e) { + System.out.println("Chunk \"" + chunkStatus.getChunkId() + "\" was interrupted!"); + return; + } } - // TODO: Mark the chunk as completed. - // TODO: Record the chunk end time in chunkStatus. - // TODO: Print a message that this chunk has finished downloading. + //Mark the chunk as completed. + chunkStatus.setCompleted(true); + + //Record the chunk end time in chunkStatus. + long endTime = System.currentTimeMillis(); + chunkStatus.setEndTimeMs(endTime); + + //Print a message that this chunk has finished downloading. + System.out.println("Chunk \"" + chunkStatus.getChunkId() + "\" has finished downloading."); } -} +} \ No newline at end of file diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 82bd239..281fb09 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,5 +1,6 @@ import java.util.ArrayList; import java.util.List; +import java.util.Scanner; public class Main { public static void main(String[] args) { @@ -19,68 +20,122 @@ public class Main { System.out.println("Chunk count: " + config.getChunkCount()); System.out.println(); + Scanner scanner = new Scanner(System.in); + System.out.println("Select Download Mode:"); + System.out.println("1. Multithreaded Mode (Parallel Downloading)"); + System.out.println("2. Sequential Mode (One by One)"); + System.out.println("3. Run Performance Benchmark (Compare Both)"); + System.out.print("Your Choice (1-3): "); + int choice = scanner.nextInt(); + + switch (choice) { + case 1 -> { + System.out.println("\n--- Starting Multithreaded Download ---"); + runMultithreaded(config); + } + case 2 -> { + System.out.println("\n--- Starting Sequential Download ---"); + runSequential(config); + } + case 3 -> { + System.out.println("\n--- Running Performance Benchmark ---"); + + System.out.println("\n[Test 1/2] Executing Sequential Mode..."); + long sequentialTime = runSequential(config); + + try { Thread.sleep(1000); } catch (InterruptedException ignored) {} + + System.out.println("\n[Test 2/2] Executing Multithreaded Mode..."); + long multithreadedTime = runMultithreaded(config); + + System.out.println("\n============================================="); + System.out.println("BENCHMARK COMPARISON REPORT"); + System.out.println("============================================="); + System.out.printf("Sequential Execution Time : %d ms%n", sequentialTime); + System.out.printf("Multithreaded Execution Time: %d ms%n", multithreadedTime); + + double speedup = (double) sequentialTime / multithreadedTime; + System.out.printf("Multithreaded Mode is %.2fx faster!%n", speedup); + System.out.println("============================================="); + } + default -> System.out.println("Invalid choice. Exiting simulation."); + } + } + + //download as multi-thread + private static long runMultithreaded(DownloadConfig config) { + long startTime = System.currentTimeMillis(); + // 2. Create chunks - List chunks = ChunkUtils.createChunks( - config.getTotalSizeMB(), - config.getChunkCount() - ); + List chunks = ChunkUtils.createChunks(config.getTotalSizeMB(), config.getChunkCount()); // 3. Create worker threads List workerThreads = new ArrayList<>(); - for (ChunkStatus chunk : chunks) { DownloadWorker worker = new DownloadWorker(chunk, config); Thread workerThread = new Thread(worker, "Worker-" + chunk.getChunkId()); - workerThreads.add(workerThread); - - // TODO: - // Students may print helpful debug information here, - // for example which chunk is assigned to which worker thread. } // 4. Create and start monitor thread ProgressMonitor monitor = new ProgressMonitor(config, chunks); Thread monitorThread = new Thread(monitor, "Progress-Monitor"); - - // TODO: - // Start the monitor thread before starting the workers - // so that progress can be displayed while downloading happens. - // - // Example idea: - // monitorThread.start(); + monitorThread.start(); // 5. Start worker threads - // TODO: - // Start each worker thread in workerThreads. - // Use a loop and call start() on each thread. + for (Thread thread : workerThreads) { + thread.start(); + } - // 6. Wait for workers to finish - // TODO: - // Wait for all worker threads to complete by calling join(). - // This should be done inside a try-catch block for InterruptedException. - // - // Hint: - // for (Thread thread : workerThreads) { - // thread.join(); - // } + // 6. Wait for workers and monitor to finish + try { + for (Thread thread : workerThreads) { + thread.join(); + } + monitorThread.join(); + System.out.println(); + } catch (InterruptedException e) { + System.out.println("Multithreaded mode interrupted."); + } - // TODO: - // After all workers finish, the monitor thread may also need to stop. - // Depending on how ProgressMonitor is implemented, students may: - // - wait for it to finish on its own, or - // - add a stopping mechanism in ProgressMonitor later. - // - // If your monitor finishes automatically, you may join it here. + long endTime = System.currentTimeMillis(); + printFinalReport(chunks, config.getTotalSizeMB()); + return (endTime - startTime); + } - // NOTE: - // this final report may show 0 progress because no worker has actually run yet. - // Until students complete the thread start/join TODOs above, + //Running download simulation sequentially + private static long runSequential(DownloadConfig config) { + long startTime = System.currentTimeMillis(); - // 7. Print final report - System.out.println(); + // 2. Create chunks + List chunks = ChunkUtils.createChunks(config.getTotalSizeMB(), config.getChunkCount()); + + // 4. Create and start monitor thread + ProgressMonitor monitor = new ProgressMonitor(config, chunks); + Thread monitorThread = new Thread(monitor, "Progress-Monitor"); + monitorThread.start(); + + // 5. Execute workers sequentially on the main thread + for (ChunkStatus chunk : chunks) { + DownloadWorker worker = new DownloadWorker(chunk, config); + worker.run(); + } + + // 6. Wait for monitor to finish logging + try { + monitorThread.join(); + System.out.println(); + } catch (InterruptedException e) { + System.out.println("Sequential mode interrupted."); + } + + long endTime = System.currentTimeMillis(); + printFinalReport(chunks, config.getTotalSizeMB()); + return (endTime - startTime); + } + + private static void printFinalReport(List chunks, int totalSizeMB) { System.out.println("=== Final Report ==="); - int completedChunks = 0; double downloadedMB = 0.0; @@ -91,17 +146,14 @@ public class Main { completedChunks++; } - System.out.println( - "Chunk " + chunk.getChunkId() - + ": " + chunk.getDownloadedMB() - + "/" + chunk.getChunkSizeMB() - + " MB" - ); + System.out.printf("Chunk %d: %.2f / %.2f MB (Completed: %b)%n", + chunk.getChunkId(), chunk.getDownloadedMB(), chunk.getChunkSizeMB(), chunk.isCompleted()); } System.out.println(); System.out.println("Completed chunks: " + completedChunks + "/" + chunks.size()); - System.out.println("Downloaded total: " + downloadedMB + "/" + config.getTotalSizeMB() + " MB"); + System.out.printf("Downloaded total: %.2f / %.2f MB%n", downloadedMB, (double) totalSizeMB); System.out.println("Simulation finished."); + System.out.println("-------------------------------------------------"); } } diff --git a/src/main/java/ProgressMonitor.java b/src/main/java/ProgressMonitor.java index 475c0e0..ef1bfb6 100644 --- a/src/main/java/ProgressMonitor.java +++ b/src/main/java/ProgressMonitor.java @@ -1,7 +1,8 @@ import java.util.List; public class ProgressMonitor implements Runnable { - + private final DownloadConfig config; + private final long startTime; private final String fileName; private final int totalSizeMB; private final List chunks; @@ -12,20 +13,15 @@ public class ProgressMonitor implements Runnable { this.totalSizeMB = config.getTotalSizeMB(); this.chunks = chunks; this.monitorDelayMs = 500; + this.startTime = System.currentTimeMillis(); + this.config = config; } @Override public void run() { - // TODO: - // Repeatedly check chunk progress until all chunks are completed. - // In each loop: - // 1. Read the downloaded size from every chunk - // 2. Add all downloaded amounts to totalDownloadedMB - // 3. Count completed chunks - // 4. Print a progress message - // 5. If completedChunks == chunks.size(), print a final monitor message and stop - // 6. Otherwise sleep for monitorDelayMs and continue - + double lastDownloadedMB = 0.0; + long lastCheckTime = System.currentTimeMillis(); + int monitorDelayMs = config.getMinStepDelayMs(); while (true) { double totalDownloadedMB = 0.0; @@ -39,10 +35,42 @@ public class ProgressMonitor implements Runnable { } } + long currentTime = System.currentTimeMillis(); + double timePassedSeconds = (currentTime - lastCheckTime) / 1000.0; + + double currentSpeed = 0.0; + if (timePassedSeconds > 0) { + currentSpeed = (totalDownloadedMB - lastDownloadedMB) / timePassedSeconds; + if (currentSpeed < 0) currentSpeed = 0; + } + + double remainingMB = config.getTotalSizeMB() - totalDownloadedMB; + String etaStr = "Calculating..."; + if (currentSpeed > 0) { + int etaSeconds = (int) (remainingMB / currentSpeed); + etaStr = String.format("%ds", etaSeconds); + } else if (remainingMB <= 0) { + etaStr = "0s"; + } + + lastDownloadedMB = totalDownloadedMB; + lastCheckTime = currentTime; + + //Progress Bar double percent = 0.0; if (totalSizeMB > 0) { percent = (totalDownloadedMB * 100.0) / totalSizeMB; } + int barLength = 30; + int filledLength = (int) (barLength * (percent / 100.0)); + + StringBuilder progressBar = new StringBuilder("["); + for (int i = 0; i < barLength; i++) { + if (i < filledLength) progressBar.append("="); + else if (i == filledLength) progressBar.append(">"); + else progressBar.append(" "); + } + progressBar.append("]"); System.out.printf( "Progress for %s: %.1f/%.1f MB (%.2f%%), completed chunks: %d/%d%n", @@ -54,9 +82,10 @@ public class ProgressMonitor implements Runnable { chunks.size() ); - - // TODO: - // If all chunks are completed, print a final message and exit the loop + if (completedChunks == chunks.size()) { + System.out.println("All chunks have completed downloading. Monitoring stopped."); + break; + } try { Thread.sleep(monitorDelayMs);