/* ArithmeticDrill: is an interactive drill that ends by reporting the
score and the time taken.  The program begins by asking the pupil a
number of questions in order to set up a drill.  This is a sample of
the start of a session.

Would you like a drill in (a)ddition or (m)ultiplication? m
How many digits do you want? 2
How many questions do you want in the drill? 2 

In this case the program generates 2 questions with two digit integers testing
multiplication.

It handles input numbers out of range with an error message but it
does not a handle non integer when it expected an integer.  

If too many digits are requested their is overflow which produces
negative answers due to two's complement integer format.  */

import java.io.*;
import java.util.*;

public class ArithmeticDrill {

  /* main: the only method in ArithmeticDrill
     creates objects of class DigitRange and RandomRange
   */

   public static void main (String[] args) throws IOException {
     DataInputStream in = new DataInputStream(System.in);
     int digits, range = 1, questions = 0, first, second, answer, correct;
     int score = 0, near = 0;
     double percent = .10;
     System.out.print("Would you like a drill in (a)ddition or (m)ultiplication? ");
     System.out.flush();
     String drillType = in.readLine();
     String op; // operator is either " + " or " - "
     boolean isAddition = drillType.equals("a");
     if (isAddition)
       op = " + ";
     else if (drillType.equals("m")) // isMultiplication
       op = " * ";
     else { // ERROR
       System.err.println("ERROR: must be a or m");
       return;
     }
     System.out.print ("How many digits do you want in the numbers? ");
     System.out.flush();
     digits = Integer.parseInt(in.readLine());
     if (digits < 1) {// || (isAddition && digits > 10 ) || (! isAddition && digits > 5)
       System.err.println("Must enter a positive number"); // <=10 for + and <=5 for *
       return;
     }
     System.out.print ("How many questions do you want? ");
     System.out.flush();
     questions = Integer.parseInt(in.readLine());
     if (questions < 1) {
       System.err.println("Must enter a positive number");
       return;
     }
     RandomRange rand = new RandomRange(new DigitRange(digits));
     long start = System.currentTimeMillis();
     for (int i=0; i < questions;i++) {
       first  = rand.nextInt();
       second = rand.nextInt();
       System.out.print(first + op + second + " = " );
       System.out.flush();
       answer = Integer.parseInt(in.readLine());
       if (isAddition)
	 correct = first + second;
       else
	 correct = first * second;
       if (answer == correct) score++;
       else {
	 System.out.println("X " + correct);
	 if (Math.abs((double)(answer - correct)/correct) < percent) {
	   near++;
	 }
       }
     }
     double elapsed = (System.currentTimeMillis() - start)/1000.0;
     System.out.println("Time elapsed:   " + elapsed + " seconds");
     System.out.println("Your score was: " + score + " out of " + questions);
     System.out.println("You were near: " + near + " out of " + questions);
     if (questions > 0) {
       System.out.println("Seconds/question " + (double)elapsed/questions);
       System.out.println("Which is " + (100.*score/questions) + " %");
       System.out.println("Near " + (100.*near/questions) + " %");
     }
   }
}

/*
DigitRange: constructs a range used by RandomRange
numberOfDigits describes 
              half open range or closed range in RandomRange
                  [lo,  hi)
1	          [ 1,  10)       [ 1,  9]
2                 [10, 100)       [10, 99]
and so on
It fails if numberOfDigits is less than 1.
 */

class DigitRange {
  private int lo, hi;
  DigitRange (int numberOfDigits) {
    int power10 = (int)Math.pow(10,numberOfDigits);
    lo = power10/10;
    hi = power10;
  }
  public int lo () { return lo;}
  public int hi () { return hi;}
}

/* RandomRange: given a DigitRange object
constructs a Random integer generator in a range
DigitRange produces random numbers in the range
[lo(), hi()-1]     [shift, width-1+shift]
It fails if only if DigitRange fails so RandomRange is reliable
*/

class RandomRange {
  private int width;
  private int shift;
  private Random rand = new Random(0);
  RandomRange (DigitRange d) {
    shift = d.lo();
    width = d.hi() - shift;
  }
  /* nextInt: 
     @returns the next integer in the range
     [shift, width-1+shift]
  */
  public int nextInt() {
    return Math.abs(rand.nextInt() % width) + shift;
  }
}

