diff --git a/README.md b/README.md
index db8a2f4ce778bad4a9c24e4af63eceb89187b327..f5135b73a923e4e33f2f855c5d395281a0e6bfb9 100644
--- a/README.md
+++ b/README.md
@@ -24,3 +24,27 @@ Proofs of Concept (PoCs that you should provide for each vulnerability):
 
 * poc/vuln-1.poc -- poc/vuln-5.poc
 
+<hr>
+Commands for testing (by Yang Liu):<br>
+<ol>
+<li>
+Generate 100 fuzz.txt<br>
+<code>
+bash ./run_fuzzer.sh
+</code>
+</li>
+<li>
+Run the generated 100 fuzz.txt above:<br>
+<code>
+bash ./run_tests.sh original
+</code><br>
+This is to run all the txt inputs on original. Replace 'original' with 'vuln-1' to 'vuln-5' as needed.
+</li>
+<li>
+Calculate coverage rate:<br>
+<code>
+bash ./get_coverage.sh ./fuzzer/fuzz.txt
+</code><br>
+Replace the './fuzzer/fuzz.txt' with the txt file used.
+</li>
+</ol>
\ No newline at end of file
diff --git a/fuzzer/Fuzzer.java b/fuzzer/Fuzzer.java
index 9394bf8fd81a260984d87d9fe1aa3f3ef35fd4de..b1ecf092bafa212a91c9debe27ee78bb441c5809 100644
--- a/fuzzer/Fuzzer.java
+++ b/fuzzer/Fuzzer.java
@@ -13,14 +13,11 @@ public class Fuzzer {
         FileOutputStream out = null;
         PrintWriter pw = null;
         try {
-            out = new FileOutputStream(OUTPUT_FILE);
-            pw = new PrintWriter(out);
-            
-            /* We just print one instruction.
-               Hint: you might want to make use of the instruction
-               grammar which is effectively encoded in Instruction.java */
-            pw.println("list");
-            
+            InputGenerator inputGenerator = new InputGenerator(
+                    1022,
+                    1024,
+                    "fuzz.txt");
+            inputGenerator.generateFuzz(InputGenerator.MODE.TOTAL_RANDOM);
         }catch (Exception e){
             e.printStackTrace(System.err);
             System.exit(1);
diff --git a/fuzzer/InputGenerator.java b/fuzzer/InputGenerator.java
index 111c459fe5275fb45448e69812cff4e72f3c9f52..7b02c6fcecba3e8fe8fbc57d1bb2ca9e49dab67b 100644
--- a/fuzzer/InputGenerator.java
+++ b/fuzzer/InputGenerator.java
@@ -1,38 +1,58 @@
+import java.io.*;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * A string generator to generate various inputs for the Fuzzer
  */
 public class InputGenerator {
 
+    // modes of the generation pattern
+    public enum MODE {
+        TOTAL_RANDOM,
+    }
+
+    // filename for the config file
+    private static final String CFG_FILE = "config.cfg";
+    // a key in the config file: how many times the fuzzer has been run already
+    private static final String keyRunCounter = "Number of runs done";
     // the chars range used to generate inputs, left inclusive, right exclusive
     // 0 is skipped since it will terminate a c string
-    private static final int[] ASCII_RANGE = new int[] {33, 127};
-
+    private static final int[] ASCII_RANGE = new int[]{33, 127};
     // how many args for the put command
     private static final int NUM_PUT_ARGS = 3;
-
     // how many times the total length of an invalid string (due to being too long) should be, relative to
     //  the max length of a valid string
     private static final int INVALID_MAX_LENGTH_MULTIPLIER = 5;
-
     // same as above, but relative to the number of arguments for a command
     private static final int INVALID_NUM_ARGUMENTS_MULTIPLIER = 10;
-
     // how likely the generated commands will have valid number of arguments
     // it will be a chance of: when random.newInt(rate) != 0 for all nun-valid arg num commands to reroll
     private static final int VALID_NUM_ARGUMENT_SKEW_RATE = 3;
-
     // how likely the generated GET commands will be asking for existing entries generated by PUT
     // the probability will be rate/(rate+1)
     private static final int GET_EXISTING_ENTRY_SKEW_RATE = 1;
-
     // avoid these chars in the randomly generated strings
-    private static final int[] AVOIDED_ASCII_IDX = new int[] {9, 10, 13, 32};
+    private static final int[] AVOIDED_ASCII_IDX = new int[]{9, 10, 13, 32};
+    // the fuzzer is assumed to run at least n times. according to the assignment description, n=10.
+    private static final int ASSUMED_MIN_TIMES_TO_RUN = 10;
 
+    // if set to true, not generating masterpw commands
+    // it seems currently there is no way to do the password check via fuzz.txt, so this is set to true
+    private static final boolean IGNORE_MASTERPW = true;
     // if set to true, will only generate commands with valid number of arguments
     // currently it seems incorrect number of arguments will end the passbook, so this is set to true
     private static final boolean ALWAYS_USE_CORRECT_NUM_ARGS = true;
+    // if set to true, will only generate commands within valid line length of given constructor arguments
+    // when set to false, will generate commands within valid line length of the original passbook code
+    private static final boolean USE_MAX_LINE_LENGTH_IN_ARGS = true;
+    // if set to true, will only generate number of commands according to the given constructor arguments
+    // when set to false, will generate number of commands according to the original passbook code
+    private static final boolean USE_MAX_NUM_LINES_IN_ARGS = true;
+    // the max line length of original passbook
+    private static final int ORIGINAL_MAX_LINE_LENGTH = 1022;
+    // the max line length of original passbook
+    private static final int ORIGINAL_MAX_NUM_LINES = 1024;
 
     // max length of an input line
     private int maxInputLineLength;
@@ -42,23 +62,113 @@ public class InputGenerator {
     private Map<String, Pair<String, String>> validPassbookEntries;
     // skipped ascii index for symbol not included in the random generated strings
     private Set<Integer> avoidedAsciiIndexSet;
+    // store config
+    private Properties configBase = new Properties();
+    // name of the output fuzz file
+    private String fuzzFileName;
+    // max number of commands in the fuzz file
+    private int maxNumLines;
 
     /**
      * base constructor
+     *
      * @param maxInputLineLength the size of input buffer for target program
+     * @param maxNumLines max number of commands in the fuzz file
+     * @param fuzzFileName       name of the output fuzz file
      */
-    public InputGenerator(int maxInputLineLength) {
-        this.maxInputLineLength = maxInputLineLength;
+    public InputGenerator(int maxInputLineLength, int maxNumLines, String fuzzFileName) {
+        this.maxInputLineLength = USE_MAX_LINE_LENGTH_IN_ARGS ? maxInputLineLength : ORIGINAL_MAX_LINE_LENGTH;
+        this.maxNumLines = USE_MAX_NUM_LINES_IN_ARGS ? maxNumLines : ORIGINAL_MAX_NUM_LINES;
+        this.fuzzFileName = fuzzFileName;
         this.random = new Random();
         this.validPassbookEntries = new HashMap<>();
         this.avoidedAsciiIndexSet = new HashSet<>();
         for (int i : AVOIDED_ASCII_IDX) {
             this.avoidedAsciiIndexSet.add(i);
         }
+        readConfig();
+    }
+
+    /**
+     * generate the fuzz file according to required generation mode
+     * @param mode the generation mode
+     */
+    public void generateFuzz(MODE mode) {
+        String output = "";
+        switch (mode) {
+            case TOTAL_RANDOM:
+                output = generateTotalRandom();
+            default:
+                output = generateTotalRandom();
+        }
+
+        // write to fuzz file
+        try {
+            BufferedWriter writer = new BufferedWriter(new FileWriter(this.fuzzFileName));
+            writer.write(output);
+            writer.close();
+            // update cfg to record times run
+            increaseRunCounter();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * generate random lines, chosen from all commands
+     *
+     * @return output
+     */
+    public String generateTotalRandom() {
+        List<String> commands = new ArrayList<>();
+        ArrayList<Instruction> instructions = new ArrayList<>();
+        for (Instruction instruction : Instruction.class.getEnumConstants()) {
+            if (IGNORE_MASTERPW && instruction == Instruction.MASTERPW) {
+                continue;
+            }
+            instructions.add(instruction);
+        }
+
+        for (int i = 0; i < this.maxNumLines; i++) {
+            int cmdIdx = random.nextInt(instructions.size());
+            switch (instructions.get(cmdIdx)) {
+                case PUT:
+                    commands.add(generatePut(1).get(0));
+                    break;
+                case GET:
+                    commands.add(generateGet(1).get(0));
+                    break;
+                case REM:
+                    commands.add(generateRem(1).get(0));
+                    break;
+                case LIST:
+                    commands.add(generateList(1).get(0));
+                    break;
+                case SAVE:
+                    commands.add(generateSave(1).get(0));
+                    break;
+                case MASTERPW:
+                    // since each masterpw has 2 lines
+                    if (commands.size() <= this.maxNumLines - 2) {
+                        ArrayList<String> masterpwCmds = generateMasterpw(1);
+                        commands.addAll(masterpwCmds);
+                        i++;
+                    }
+                    break;
+            }
+        }
+
+        // make sure the output does not crash the program by having too many lines
+        if (commands.size() > this.maxNumLines) {
+            commands = commands.subList(0, this.maxNumLines - 1);
+        }
+        String output = commands.stream().map(Objects::toString).collect(Collectors.joining("\n"));
+        return output;
     }
 
     /**
      * generate multiple random PUT commands
+     *
      * @param numCommands number of commands to generate
      * @return all the commands generated
      */
@@ -68,6 +178,7 @@ public class InputGenerator {
 
     /**
      * generate multiple random REM commands
+     *
      * @param numCommands number of commands to generate
      * @return all the commands generated
      */
@@ -77,6 +188,7 @@ public class InputGenerator {
 
     /**
      * generate multiple random GET commands
+     *
      * @param numCommands number of commands to generate
      * @return all the commands generated
      */
@@ -86,6 +198,7 @@ public class InputGenerator {
 
     /**
      * generate multiple random LIST commands
+     *
      * @param numCommands number of commands to generate
      * @return all the commands generated
      */
@@ -95,6 +208,7 @@ public class InputGenerator {
 
     /**
      * generate multiple random SAVE commands
+     *
      * @param numCommands number of commands to generate
      * @return all the commands generated
      */
@@ -105,6 +219,7 @@ public class InputGenerator {
     /**
      * generate multiple random MASTERPW commands
      * each 'masterpw password' will be followed by the password itself.
+     *
      * @param numCommands number of commands to generate
      * @return all the commands generated
      */
@@ -116,7 +231,7 @@ public class InputGenerator {
             String pw = prompt.split(" ")[1];
             // trim a bit in case the pw is too long and only part of it is registered in the buffer
             // this has to take into account of the length of 'masterpw ' part in the front
-            int maxPwLength = maxInputLineLength - Instruction.MASTERPW.getOpcode().length() - 2;
+            int maxPwLength = maxInputLineLength - Instruction.MASTERPW.getOpcode().length() - 1;
             if (pw.length() > maxPwLength) {
                 pw = pw.substring(0, maxPwLength);
             }
@@ -128,19 +243,20 @@ public class InputGenerator {
 
     /**
      * generate multiple random commands based on url as the first argument, so stored url
-     *      generated by 'put' can be tested
+     * generated by 'put' can be tested
      * currently these commands are just 'rem' and 'get'
-     * @param command the Instruction
-     * @param numCommands number of commands to generate
+     *
+     * @param command       the Instruction
+     * @param numCommands   number of commands to generate
      * @param noMutateRatio see mutateRandomCmdWithValidUrl()
-     * @param attachRatio see mutateRandomCmdWithValidUrl()
-     * @param replaceRatio see mutateRandomCmdWithValidUrl()
+     * @param attachRatio   see mutateRandomCmdWithValidUrl()
+     * @param replaceRatio  see mutateRandomCmdWithValidUrl()
      * @return generated commands
      */
     private ArrayList<String> generateUrlBasedCommands(Instruction command, int numCommands,
-                                                      int noMutateRatio,
-                                                      int attachRatio,
-                                                      int replaceRatio) {
+                                                       int noMutateRatio,
+                                                       int attachRatio,
+                                                       int replaceRatio) {
         // if no stored entries, generate random ones
         if (validPassbookEntries.isEmpty()) {
             return generateCommands(command, numCommands);
@@ -162,6 +278,7 @@ public class InputGenerator {
             return commands;
         }
     }
+
     // overload with default arguments
     private ArrayList<String> generateUrlBasedCommands(Instruction command, int numCommands) {
         return generateUrlBasedCommands(command, numCommands, 1, 1, 1);
@@ -170,16 +287,17 @@ public class InputGenerator {
     /**
      * mutate the random commands, make some of them to have part of a stored url
      * currently have 3 random cases:
-     *      0. no change
-     *      1. attach a stored url to the first argument, e.g. we have a url of --url1--, and the
-     *          random command is 'get abcde ff', the result will be 'get --url1--abcde ff'
-     *      2. replace the first argument with a stored url. the result of the above example will
-     *          become 'get --url1-- ff'
+     * 0. no change
+     * 1. attach a stored url to the first argument, e.g. we have a url of --url1--, and the
+     * random command is 'get abcde ff', the result will be 'get --url1--abcde ff'
+     * 2. replace the first argument with a stored url. the result of the above example will
+     * become 'get --url1-- ff'
      * used by commands that are based on urls generated by PUT
+     *
      * @param randomCommands total random commands
-     * @param noMutateRatio ratio of case 0
-     * @param attachRatio ratio of case 1
-     * @param replaceRatio ratio of case 2
+     * @param noMutateRatio  ratio of case 0
+     * @param attachRatio    ratio of case 1
+     * @param replaceRatio   ratio of case 2
      * @return the commands, with some of them mutated
      */
     private ArrayList<String> mutateRandomCmdWithValidUrl(ArrayList<String> randomCommands,
@@ -188,7 +306,7 @@ public class InputGenerator {
                                                           int replaceRatio) {
         int totalRatio = noMutateRatio + attachRatio + replaceRatio;
         if (totalRatio == 0) {
-            return  randomCommands;
+            return randomCommands;
         }
 
         ArrayList<String> mutatedCommands = new ArrayList<>();
@@ -206,6 +324,10 @@ public class InputGenerator {
                     cmdArray[1] = getRandomUrlFromStored() + cmdArray[1];
                     mutatedCmd = String.join(" ", cmdArray);
                 }
+                // make sure the mutation will not make the line too long
+                if (mutatedCmd.length() > this.maxInputLineLength) {
+                    mutatedCmd = mutatedCmd.substring(0, this.maxInputLineLength);
+                }
                 mutatedCommands.add(mutatedCmd);
             } else {
                 // case 2: replace the first argument with valid url
@@ -215,6 +337,10 @@ public class InputGenerator {
                     cmdArray[1] = getRandomUrlFromStored();
                     mutatedCmd = String.join(" ", cmdArray);
                 }
+                // make sure the mutation will not make the line too long
+                if (mutatedCmd.length() > this.maxInputLineLength) {
+                    mutatedCmd = mutatedCmd.substring(0, this.maxInputLineLength);
+                }
                 mutatedCommands.add(mutatedCmd);
             }
         }
@@ -223,6 +349,7 @@ public class InputGenerator {
 
     /**
      * obtain a random stored url form recorded validPassbookEntries
+     *
      * @return the url
      */
     private String getRandomUrlFromStored() {
@@ -236,7 +363,8 @@ public class InputGenerator {
 
     /**
      * generate multiple random given commands, with randomness in number of arguments and input length
-     * @param command the command
+     *
+     * @param command     the command
      * @param numCommands number of commands to generate
      * @return all the commands generated
      */
@@ -253,7 +381,8 @@ public class InputGenerator {
 
     /**
      * generate random number of random arguments, then attach those after a command
-     * @param command the command
+     *
+     * @param command      the command
      * @param numValidArgs valid number of args for the command
      * @return a string, showing the command with arguments
      */
@@ -288,7 +417,10 @@ public class InputGenerator {
         if (mode == numValidArgs + 2) {
             mode = numValidArgs * INVALID_NUM_ARGUMENTS_MULTIPLIER;
         }
-        String randomLengthString = generateRandomLengthString(mode, maxInputLineLength);
+
+        // take into account of the length of instruction itself and spaces between args
+        int maxLineLengthToUse = this.maxInputLineLength - (command.length() + mode);
+        String randomLengthString = generateRandomLengthString(mode, maxLineLengthToUse);
         ArrayList<String> argumentsList = stringSeparator(randomLengthString, mode);
         // retry if failed for some reason
         if (argumentsList == null) {
@@ -307,23 +439,29 @@ public class InputGenerator {
     }
 
     /**
-     * generate a string with length of the given length, or more than the given length, randomly
+     * generate a string with length of the given length
+     * can also randomly generate lines with greater length, but currently not in use,
+     *      since currently line length greater than the max line length will crash the program
+     *
      * @param minLength valid min length
      * @param maxLength valid max length
      * @return a string with valid length or more than valid length
      */
     private String generateRandomLengthString(int minLength, int maxLength) {
-        int mode = random.nextInt(2);
-        if (mode == 0) {
-            return generateRandomString(minLength, maxLength);
-        } else {
-            return generateRandomString(maxLength + 1, (maxLength + 1) * INVALID_MAX_LENGTH_MULTIPLIER);
-        }
+        return generateRandomString(minLength, maxLength);
+        // commended out: since currently line length greater than the max line length will crash the program
+//        int mode = random.nextInt(2);
+//        if (mode == 0) {
+//            return generateRandomString(minLength, maxLength);
+//        } else {
+//            return generateRandomString(maxLength + 1, (maxLength + 1) * INVALID_MAX_LENGTH_MULTIPLIER);
+//        }
     }
 
     /**
      * generate a random string with ascii symbols in the ASCII_RANGE, of given length range
      * including both minLength and maxLength
+     *
      * @param minLength min length of the string
      * @param maxLength man length of the string
      * @return a random string. an empty one if minLength and maxLength are not matched correctly
@@ -350,7 +488,8 @@ public class InputGenerator {
     /**
      * separate a string into numSections parts, each with random length
      * will not output empty strings as parts
-     * @param input input string
+     *
+     * @param input       input string
      * @param numSections number of output sections
      * @return a list of string sections if possible, null otherwise
      */
@@ -394,10 +533,57 @@ public class InputGenerator {
 
     /**
      * get the stored valid entries
+     *
      * @return this.validPassbookEntries
      */
     Map<String, Pair<String, String>> getValidPassbookEntries() {
         return validPassbookEntries;
     }
 
+    /**
+     * read config file. initialize a new one if it doesn't exit
+     * if for some reason an IO error occurred, use random values instead
+     */
+    private void readConfig() {
+        File cfgFile = new File(CFG_FILE);
+        try {
+            if (cfgFile.exists()) {
+                this.configBase.load(new FileInputStream(CFG_FILE));
+            } else {
+                setDefaultConfig(false);
+                this.configBase.store(new FileOutputStream(CFG_FILE), null);
+            }
+        } catch (IOException e) {
+            setDefaultConfig(true);
+        }
+    }
+
+    /**
+     * setup default config values in self.configBase
+     *
+     * @param useRandom whether to use random value for default
+     */
+    private void setDefaultConfig(boolean useRandom) {
+        int timesRun = 0;
+        if (useRandom) {
+            timesRun = random.nextInt(ASSUMED_MIN_TIMES_TO_RUN);
+        }
+        this.configBase.setProperty(keyRunCounter, Integer.toString(timesRun));
+    }
+
+    /**
+     * increase the run counter, write to cfg file
+     *
+     * @param numChange change of run counter
+     */
+    private void increaseRunCounter(int numChange) throws IOException {
+        int runCounter = Integer.parseInt(this.configBase.getProperty(keyRunCounter, "0"));
+        runCounter += numChange;
+        this.configBase.setProperty(keyRunCounter, Integer.toString(runCounter));
+        this.configBase.store(new FileWriter(CFG_FILE), null);
+    }
+    // overload with default args
+    private void increaseRunCounter() throws IOException {
+        increaseRunCounter(1);
+    }
 }