private static void RunSingleIteration(TimekeepingDictionary <StudentPair> tkDict)
        {
            // Generate a list of all students
            List <int> allStudentIds = Enumerable.Range(1, checked (RowCount * ColumnCount)).ToList();

            // Randomly place them throughout the classroom
            int[,] studentPlacements = new int[RowCount, ColumnCount];
            for (int i = 0; i < RowCount; i++)
            {
                for (int j = 0; j < ColumnCount; j++)
                {
                    studentPlacements[i, j] = allStudentIds.ChooseAndRemoveRandom();
                }
            }

            Debug.Assert(allStudentIds.Count == 0, "Should've placed all students.");

            // Now calculate exposure levels

            TimeSpan adjacentExposure = TimeSpan.FromTicks((long)(IterationTime.Ticks * AdjacentExposureFactor));
            TimeSpan diagonalExposure = TimeSpan.FromTicks((long)(IterationTime.Ticks * DiagonalExposureFactor));

            // Calculate exposure left-right

            for (int i = 0; i < RowCount - 1; i++)
            {
                for (int j = 0; j < ColumnCount; j++)
                {
                    tkDict.AddTime(new StudentPair(studentPlacements[i, j], studentPlacements[i + 1, j]), adjacentExposure);
                }
            }

            // Calculate exposure up-down

            for (int i = 0; i < RowCount; i++)
            {
                for (int j = 0; j < ColumnCount - 1; j++)
                {
                    tkDict.AddTime(new StudentPair(studentPlacements[i, j], studentPlacements[i, j + 1]), adjacentExposure);
                }
            }

            // Calculate exposure diagonally

            for (int i = 0; i < RowCount - 1; i++)
            {
                for (int j = 0; j < ColumnCount - 1; j++)
                {
                    tkDict.AddTime(new StudentPair(studentPlacements[i, j], studentPlacements[i + 1, j + 1]), diagonalExposure);
                    tkDict.AddTime(new StudentPair(studentPlacements[i + 1, j], studentPlacements[i, j + 1]), diagonalExposure);
                }
            }
        }
        /// <summary>
        /// Runs the simulation, returning the number of students who were over-exposed
        /// to a particular student (for simplicity, student id #1).
        /// </summary>
        public static int RunSimulationForOneStudent()
        {
            // Run the simulation (several iterations)
            TimekeepingDictionary <StudentPair> tkDict = new TimekeepingDictionary <StudentPair>();

            for (int i = 0; i < IterationCount; i++)
            {
                RunSingleIteration(tkDict);
            }

            // Find each pair who exceeded "risky" exposure level, and count the
            // number of pairs which involve student #1.

            return(tkDict
                   .Where(entry => entry.Value >= ExposureRiskThreshold)
                   .Where(entry => entry.Key.GetStudentIds().Contains(1))
                   .Select(entry => entry.Key)
                   .Distinct()
                   .Count());
        }
        /// <summary>
        /// Runs the simulation, returning the number of students (not student pairs)
        /// whose total exposure to any other single student exceeds the specified
        /// CDC risk leel.
        /// </summary>
        public static int RunSimulationForAllStudents(out bool wasTrackedStudentOverexposed)
        {
            // Run the simulation (several iterations)
            TimekeepingDictionary <StudentPair> tkDict = new TimekeepingDictionary <StudentPair>();

            for (int i = 0; i < IterationCount; i++)
            {
                RunSingleIteration(tkDict);
            }

            // Now count the number of students who exceeded risky exposure level
            HashSet <int> idsOfStudentsAtRisk = tkDict
                                                .Where(entry => entry.Value >= ExposureRiskThreshold)
                                                .SelectMany(entry => entry.Key.GetStudentIds())
                                                .ToHashSet();

            // Assuming we're tracking a single student (say, student id #1),
            // out a value stating whether this particular student was overexposed
            // to any other student.
            wasTrackedStudentOverexposed = idsOfStudentsAtRisk.Contains(1);
            return(idsOfStudentsAtRisk.Count);
        }