/*************************************************************************/
//  CS 100b
//  Fall 1999
//  Homework 3 -- Interval Arithmetic
//
//  This is a program to demonstrate the use of an Interval class for
//  doing interval arithmetic.  The interval class allows interval objects to
//  be added, subtracted, multiplied, and divided to other interval objects
//  using the well known rules of interval math (as passed on by Professor
//  Dave Schwartz).  Support for these operations with doubles is also
//  contained in the Interval class.  The Interval class also provides methods
//  to get interval minimum and maximum (and print these quantities),
//  name the intervals that are created (via a constructor argument), and
//  compute the length of an interval.  Additional functionalities may
//  be added at a later time and those functionalities which do currently
//  exist are not guaranteed to be correct!
//
//
//  As a working example of how everything comes together to solve an
//  engineering-type problem, the three interval objects have been created
//  and are used to solve a problem in circuit analysis.  The example
//  was obtained from Professor Schwartz so all specific questions should
//  be directed towards him!  :)
/*************************************************************************/

import java.awt.*;
import java.awt.event.*;

public class Homework3 {
    public static void main(String[] args) {
	amplifier();
    }

    public static void amplifier() {

	// variable to keep track of interval sizes
	double xLeft = 0, xRight = 0;
	double yLeft = 0, yRight = 0;
	double zLeft = 0, zRight = 0;

	// multiplier -- multiplying factor when testing code
	double multiplier = 0;

	// input type -> user can enter either intervals or interval
	//               center-points and associated uncertainties
	String inputType = null;

	// for testing code, which operation (i.e., add, subtract, mult)
	// wantes to be tested
	String operation = null;

	// token reader stuff for i/o
	TokenReader stdin = new TokenReader(System.in);

	// input whether one is testing code or trying to solve
	// the amplifier problem
	System.out.println("Test code (t) or solve amplifier problem (a)?");
	String problem = stdin.readString();

	if(problem.equals("t")) {
	    problem = "test";
	    inputType = "i";

	    // see which operation wants to be checked (with interval
	    // specified below)
	    System.out.println("Check which operation (add), (subtract), " +
			       "(mult)?");
	    operation = stdin.readString();
	}
	else if(problem.equals("a")) {
	    problem = "amplifier";
	    inputType = "c";
	}

	// if testing code, enter two intervals explicitly.  if solving
	// amplifier problem, enter center-point and uncertainty of
	// three intervals

	if(inputType.equals("i")) {
	    System.out.println("Enter interval one");
	    xLeft = stdin.readDouble();
	    xRight = stdin.readDouble();

	    // if testing mult, only need one interval and a multiplier
	    if(operation.equals("mult")) {
		System.out.println("Enter multiplier");
		multiplier = stdin.readDouble();
	    }
	    else {
		System.out.println("Enter interval two");
		yLeft = stdin.readDouble();
		yRight = stdin.readDouble();
	    }
	}
	else if(inputType.equals("c")) {
	    System.out.println("Enter A center-point");
	    double aCenter = stdin.readDouble();

	    System.out.println("Enter A uncertainty");
	    double aUncertain = stdin.readDouble();

	    System.out.println("Enter R1 center-point");
	    double r1Center = stdin.readDouble();

	    System.out.println("Enter R1 uncertainty");
	    double r1Uncertain = stdin.readDouble();

	    System.out.println("Enter R2 center-point");
	    double r2Center = stdin.readDouble();

	    System.out.println("Enter R2 uncertainty");
	    double r2Uncertain = stdin.readDouble();

	    // compute the intervals based on the above interval info.
	    xLeft = aCenter*(1 - aUncertain);
	    xRight = aCenter*(1 + aUncertain);

	    yLeft = r1Center*(1 - r1Uncertain);
	    yRight = r1Center*(1 + r1Uncertain);

	    zLeft = r2Center*(1 - r2Uncertain);
	    zRight = r2Center*(1 + r2Uncertain);
	}

	// if testing code, run three test cases (as described in
	// homework packet)

	/**************************************************************/
	// test the code
	/**************************************************************/

	if(problem.equals("test")) {

	    // construct two interval objects
	    Interval myInterval = new Interval();
	    Interval myInterval2 = new Interval();
	    Interval myInterval3;

	    // do example 5.2a

	    if(operation.equals("add")) {

		// set size of myInterval (using values entered from above)
		myInterval.setIntervalMinimum(xLeft);
		myInterval.setIntervalMaximum(xRight);

		// set size of myInterval2 (using values entered from above);
		myInterval2.setIntervalMinimum(yLeft);
		myInterval2.setIntervalMaximum(yRight);

		// add myInterval to myInterval2
		myInterval3 = myInterval2.addInterval(myInterval);

		System.out.println("\nAfter adding two intervals.\n");

		// print extent of myInterval2 (after adding)
		myInterval3.printInterval();
	    }
	    else if(operation.equals("subtract")) {

		// do next example 5.2b

		// set size of myInterval
		myInterval.setIntervalMinimum(xLeft);
		myInterval.setIntervalMaximum(xRight);

		// set size of myInterval2
		myInterval2.setIntervalMinimum(yLeft);
		myInterval2.setIntervalMaximum(yRight);

		// subtract myInterval to myInterval2
		myInterval3 = myInterval2.subtractInterval(myInterval);

		System.out.println("\nAfter subtracting two intervals.\n");

		// print extent of myInterval2 (after subtracting)
		myInterval3.printInterval();
	    }
	    else if(operation.equals("mult")) {

		// set size of myInterval
		myInterval.setIntervalMinimum(xLeft);
		myInterval.setIntervalMaximum(xRight);

		// do example 5.2c
		myInterval3 = myInterval.multiplyInterval(multiplier);

		System.out.println("\nAfter multiplying two intervals.\n");

		// print extent of myInterval2 (after multiplying)
		myInterval3.printInterval();
	    }

	}

	/**************************************************************/
	// now solve the amplifier problem
	/**************************************************************/

	else if(problem.equals("amplifier")) {

	    // set up intervals
	    Interval A = new Interval(xLeft, xRight);

	    Interval R1 = new Interval(yLeft, yRight);
	    Interval R2 = new Interval(zLeft, zRight);

	    // add intervals R1 + R2
	    Interval sum = R1.addInterval(R2);

	    // divide intervals R1/(R1 + R2)
	    Interval quotient = R1.divideInterval(sum);

	    // create interval [1,1]
	    Interval one = new Interval(1,1);

	    // compute inverse of A
	    Interval Ainverse = one.divideInterval(A);

	    // add to compute denominator
	    Interval denominator = Ainverse.addInterval(quotient);

	    // compute inverse of this (since fraction is in denominator)
	    Interval answer = one.divideInterval(denominator);

	    // print answer and we're done....wasn't that fun!
	    System.out.println();
	    answer.printInterval();
	    System.out.println();
	}
    }
}

/*******************************************************************/

// This is my interval class which contains the appropriate methods

class Interval {

    // data for Interval class
    private double minimum;
    private double maximum;

    // empty constructor
    public Interval() {}

    // another constructor (set interval size when object is constructed)
    public Interval(double minimum, double maximum) {
	this.minimum = minimum;
	this.maximum = maximum;
    }

    // set/get interval minimum
    public void setIntervalMinimum(double minimum) {
	this.minimum = minimum;
    }

    public double getIntervalMinimum() {
	return minimum;
    }

    // set/get interval maximum
    public void setIntervalMaximum(double maximum) {
	this.maximum = maximum;
    }

    public double getIntervalMaximum() {
	return maximum;
    }

    // print interval
    public void printInterval() {
	System.out.println("The answer is:  [" + minimum + ", " +
			   maximum + "]\n");
    }

    /***********************************************************************/
    // the following are the rules for adding, subtracting, multiplying, and
    // dividing intervals.  the methods include rules for these operations
    // with themselves (i.e., intervals-and-intervals) and primitive types
    // (i.e., doubles-and-intervals)
    /***********************************************************************/

    // add two interval objects (Java doesn't support operator overloading
    // so we need a method to do it)
    public Interval addInterval(Interval addedInterval) {
	double tempMin = minimum + addedInterval.getIntervalMinimum();
	double tempMax = maximum + addedInterval.getIntervalMaximum();

	Interval tempInterval = new Interval();
	tempInterval.setIntervalMinimum(tempMin);
	tempInterval.setIntervalMaximum(tempMax);

	return tempInterval;
    }

    // add an interval with a double
    public Interval addInterval(double add) {
	double tempMin = minimum + add;
	double tempMax = maximum + add;

	Interval tempInterval = new Interval();
	tempInterval.setIntervalMinimum(tempMin);
	tempInterval.setIntervalMaximum(tempMax);

	return tempInterval;
    }

    // substract two interval objects
    public Interval subtractInterval(Interval subtractedInterval) {
	double tempMin = minimum - subtractedInterval.getIntervalMaximum();
	double tempMax = maximum - subtractedInterval.getIntervalMinimum();

	Interval tempInterval = new Interval();
	tempInterval.setIntervalMinimum(tempMin);
	tempInterval.setIntervalMaximum(tempMax);

	return tempInterval;
    }

    // subtract double from interval
    public Interval subtractInterval(double sub) {
	double tempMin = minimum + sub;
	double tempMax = maximum + sub;

	Interval tempInterval = new Interval();
	tempInterval.setIntervalMinimum(tempMin);
	tempInterval.setIntervalMaximum(tempMax);

	return tempInterval;
    }

    // multiply interval by scalar
    public Interval multiplyInterval(double mult) {
	double tempMin = minimum*mult;
	double tempMax = maximum*mult;

	Interval tempInterval = new Interval();
	tempInterval.setIntervalMinimum(tempMin);
	tempInterval.setIntervalMaximum(tempMax);

	return tempInterval;
    }


    // multiply two interval objects
    public Interval multiplyInterval(Interval multInterval) {

	double tempMin = Math.min(Math.min(Math.min(minimum*multInterval.
						    getIntervalMinimum(),
						    minimum*multInterval.
						    getIntervalMaximum()),
					   maximum*multInterval.
					   getIntervalMinimum()),
				  maximum*multInterval.getIntervalMaximum());

	double tempMax = Math.max(Math.max(Math.max(minimum*multInterval.
						    getIntervalMinimum(),
						    minimum*multInterval.
						    getIntervalMaximum()),
					   maximum*multInterval.
					   getIntervalMinimum()),
				  maximum*multInterval.getIntervalMaximum());

	Interval tempInterval = new Interval();
	tempInterval.setIntervalMinimum(tempMin);
	tempInterval.setIntervalMaximum(tempMax);

	return tempInterval;
    }

    // divide two interval objects
    public Interval divideInterval(Interval divInterval) {

	// check for zero min/max of dividing interval
	if(divInterval.getIntervalMinimum() == 0 ||
	   divInterval.getIntervalMaximum() == 0) {
	    System.out.println("The interval you're dividing by has a " +
			       "zero minimum or maximum!!");
	    System.exit(123);
	    return null;
	}
	else {
	    double tempMin = Math.min(Math.min(Math.min(minimum/divInterval.
							getIntervalMinimum(),
							minimum/divInterval.
							getIntervalMaximum()),
					       maximum/divInterval.
					       getIntervalMinimum()),
				      maximum/divInterval.
				      getIntervalMaximum());

	    double tempMax = Math.max(Math.max(Math.max(minimum/divInterval.
							getIntervalMinimum(),
							minimum/divInterval.
							getIntervalMaximum()),
					       maximum/divInterval.
					       getIntervalMinimum()),
				      maximum/divInterval.
				      getIntervalMaximum());

	    Interval tempInterval = new Interval();
	    tempInterval.setIntervalMinimum(tempMin);
	    tempInterval.setIntervalMaximum(tempMax);

	    return tempInterval;
	}
    }
}
