import java.util.*;
interface ScorableWork
{
Integer getScore();
int getStudentId();
}
class StudentComparator implements Comparator<ScorableWork> {
public int compare(final ScorableWork sw1, final ScorableWork sw2) {
final int score1 = sw1.getScore();
final int score2 = sw2.getScore();
if (score1 > score2) return -1;
else if (score1 < score2) return 1;
else {
final int id1 = sw1.getStudentId();
final int id2 = sw2.getStudentId();
if (id1 < id2) return -1;
else if (id1 > id2) return 1;
else return 0;
}
}
}
class StudentAnswers implements ScorableWork
{
private final int studentId;
private final int[] answerArray;
private Integer score = null;
public StudentAnswers(final int studentId, final int[] answerArray) {
this.studentId = studentId;
this.answerArray = answerArray;
}
public StudentAnswers gradeUsing(AnswerKey key) {
score = key.countNumberCorrect(answerArray);
return this;
}
public int getStudentId() {
return studentId;
}
public Integer getScore() {
return score;
}
public Integer getPercentCorrect() {
if (score == null) return null;
return (score * 100) / answerArray.length;
}
public String toString() {
final StringBuilder sb = new StringBuilder("Student #" + studentId + ":");
for (int answer : answerArray) {
sb.append(" " + answer);
}
sb.append(" " + getPercentCorrect() + "%");
return sb.toString() + "\n";
}
}
class AnswerKey
{
private final int[] answerArray;
public AnswerKey(final int[] answerArray) {
this.answerArray = answerArray;
}
public int countNumberCorrect(final int[] answerArray) {
if (answerArray == null || this.answerArray == null) return 0;
final int n = Math.min(answerArray.length, this.answerArray.length);
int score = 0;
for (int i = 0; i < n; i++) {
if (this.answerArray[i] == answerArray[i]) score++;
}
return score;
}
public String toString() {
final StringBuilder sb = new StringBuilder("Answer Key:");
for (int answer : answerArray) {
sb.append(" " + answer);
}
return sb.toString() + "\n";
}
}
class Exam
{
private final static StudentComparator comparator = new StudentComparator();
private final int examId;
private final AnswerKey answerKey;
private final List<StudentAnswers> studentAnswersList;
private final int numQuestions;
private final int numStudents;
public Exam(final int examId, final int[][] answerGrid) {
this.examId = examId;
numQuestions = answerGrid[0].length;
numStudents = answerGrid.length - 1;
answerKey = new AnswerKey(answerGrid[0]);
studentAnswersList = new ArrayList<StudentAnswers>();
for (int i = 0; i < numStudents; i++) {
final int studentId = i + 1;
final StudentAnswers studentAnswers = new StudentAnswers(studentId, answerGrid[studentId]);
studentAnswersList.add(studentAnswers.gradeUsing(answerKey));
}
Collections.sort(studentAnswersList, comparator);
}
public String toString() {
final StringBuilder sb = new StringBuilder(answerKey.toString());
for (StudentAnswers studentAnswers: studentAnswersList) {
sb.append(studentAnswers.toString());
}
return sb.toString();
}
}
abstract class ExamAnswerCube
{
protected final int
numExams,
numChoices,
numQuestions,
numStudents;
private final int[][][] answerCube;
private final List<Exam> exams = new ArrayList<Exam>();
public ExamAnswerCube(final int numChoices, final int numQuestions, final int numStudents, final int numExams) {
this.numChoices = numChoices;
this.numQuestions = numQuestions;
this.numStudents = numStudents;
this.numExams = numExams;
answerCube = getAllAnswerSetsForAllExams();
int examId = 0;
for (int[][] answerGrid : answerCube)
exams.add(new Exam(++examId, answerGrid));
}
/** Returns a 3-dimensional array of answers.
*
* First dimension:
* answerSets[0] is the set of answer sets for the first exam.
* answerSets[1] is the set of answer sets for the second exam.
* etc.
*
* Second dimension:
* answerSets[i][0] is the answer key for an exam.
* answerSets[i][j], j > 0, is the jth student's set of answers.
*
* Third dimension:
* answerSets[i][j][0] is an answer to the first question of an exam.
* answerSets[i][j][1] is an answer to the second question of an exam.
* etc.
*/
private int[][][] getAllAnswerSetsForAllExams() {
int[][][] answerSets = new int[numExams][][];
for (int i = 0; i < numExams; i = i + 1) {
answerSets[i] = getAllAnswerSetsForOneExam();
}
return answerSets;
}
private int[][] getAllAnswerSetsForOneExam() {
int[][] answerSets = new int[numStudents + 1][];
answerSets[0] = getAnswerKey();
for (int i = 1; i <= numStudents; i = i + 1) {
answerSets[i] = getStudentAnswers();
}
return answerSets;
}
protected abstract int[] getAnswerKey();
protected abstract int[] getStudentAnswers();
public String toString() {
StringBuilder sb = new StringBuilder();
for (Exam exam : exams) sb.append(exam.toString() + "\n");
return sb.toString();
}
}
final class RandomExamAnswerCube extends ExamAnswerCube
{
public RandomExamAnswerCube(final int numChoices, final int numQuestions, final int numStudents, final int numExams) {
super(numChoices, numQuestions, numStudents, numExams);
}
protected int[] getAnswerKey() {
return getRandomAnswers();
}
protected int[] getStudentAnswers() {
return getRandomAnswers();
}
private int[] getRandomAnswers() {
// An answer is an integer in the range [0, numChoices).
// answerSet[0] is the answer for the first question.
// answerSet[1] is the answer for the second question.
// etc.
int[] answerSet = new int[numQuestions];
for (int i = 0; i < numQuestions; i = i + 1) {
answerSet[i] = (int)(Math.random() * numChoices);
}
return answerSet;
}
}
public class MyClass
{
public static void main(final String args[]) {
final int
n = args.length,
numChoices = n > 0 ? Integer.parseInt(args[0]) : 5,
numQuestions = n > 1 ? Integer.parseInt(args[1]) : 10,
numStudents = n > 2 ? Integer.parseInt(args[2]) : 25,
numExams = n > 3 ? Integer.parseInt(args[3]) : 1;
ExamAnswerCube cube = new RandomExamAnswerCube(numChoices, numQuestions, numStudents, numExams);
System.out.println(cube);
}
}