package jp.ac.takushoku_u.cs;

/**
* Rangeクラスは範囲を保持するためのクラスです。
* 最大値および、最小値が NaN(非数) である場合は「存在する区間なし」を表します。
* @version 1.01
* @author 笠島 裕史(Kasajima Hiroshi)
*/
/*
* 最終更新日 2005年01月07日
*/
public class Range{
	
/**
* 最大値を保持します。
*/
	private double maxValue;
/**
* 最小値を保持します。
*/
	private double minValue;
/**
* 最大値は範囲に含まれるかを示します。
*/
	private boolean includeMax;
/**
* 最小値は範囲に含まれるかを示します。
*/
	private boolean includeMin;
/**
* 存在する区間なしかどうか示します。
*/
	private boolean empty = false;
	
/**
* 存在する区間なしのときの最大値。
*/
	private static final double EMPTY_MAX = Double.NaN;
/**
* 存在する区間なしのときの最小値。
*/
	private static final double EMPTY_MIN = Double.NaN;
	
/**
* 存在する区間が無い Range を設定します。
*/
	public Range(){
		createEmpty();
	}
	
/**
* 区間を設定した Range を設定します。両端は区間に含まれます。<BR>
* 最小値と最大値のいずれかがNaNである場合は「存在する区間なし」となります。
* @param min 最小値
* @param max 最大値
* @exception IllegalRelationValuesException 最大値と最小値の関係が異なる場合
*/
	public Range(double min, double max){
		
		if(isExistRange(min, max)){
			setMax(max);
			setMin(min);
			setEmpty(false);
			setHasMax(true);
			setHasMin(true);
		}
		else if(Double.isNaN(min) || Double.isNaN(max)){
			createEmpty();
		}
		else{
			throw new IllegalRelationValuesException("min,max("+min+","+max+")");
		}
	}
	
/**
* 区間を設定した Range を設定します。<BR>
* 最小値と最大値のいずれかがNaNである場合は「存在する区間なし」となります。
* @param min 最小値
* @param max 最大値
* @param isIncludeMin 最小値を区間内に含むか
* @param isIncludeMax 最大値を区間内に含むか
* @exception IllegalRelationValuesException 最大値と最小値の関係が異なる場合
*/
	public Range(double min, double max, boolean isIncludeMin, boolean isIncludeMax){
		
		if(isExistRange(min, max)){
			setMax(max);
			setMin(min);
			setEmpty(false);
			if(max != min){
				setHasMax(isIncludeMax);
				setHasMin(isIncludeMin);
			}
			else{
				setHasMax(true);
				setHasMin(true);
			}
		}
		else if(Double.isNaN(min) || Double.isNaN(max)){
			createEmpty();
		}
		else{
			throw new IllegalRelationValuesException("min,max("+ min +","+ max +")");
		}
	}
	
/**
* 区間の最大値を返します。<BR>
* 「存在する区間なし」の場合はNaNが返されます。
* @return 最大値
*/
	public double getMax(){
		return maxValue;
	}
	
/**
* 区間の最小値を返します。<BR>
* 「存在する区間なし」の場合はNaNが返されます。
* @return 最小値
*/
	public double getMin(){
		return minValue;
	}
	
/**
* 最大値は区間に含むか返します。
* @return 区間に含まれれば true
*/
	public boolean hasMax(){
		return includeMax;
	}
	
/**
* 最小値は区間に含むか返します。
* @return 区間に含まれれば true
*/
	public boolean hasMin(){
		return includeMin;
	}
	
/**
* 存在する区間なしどうか返します。
* @return 存在する区間なしならば true
*/
	public boolean isEmpty(){
		return empty;
	}
	
/**
* 最大値と最小値が等しいかどうか返します。
* @return 最大値と最小値が等しければ true
*/
	public boolean isSame(){
		return getMax() == getMin();
	}
	
/**
* 指定した値が区間に含まれているかチェックします。
* @param value 指定した値
* @return 指定した値が含まれていれば true
*/
	public boolean hasValue(double value){
		boolean bo;
		if(isEmpty()){
			bo = false;
		}
		else if(getMin() < value && value < getMax()){
			bo = true;
		}
		else if(getMin() == value && hasMin()){
			bo = true;
		}
		else if(getMax() == value && hasMax()){
			bo = true;
		}
		else if(value == 0.0 && (getMin() == -0.0 || getMax() == -0.0)){
			bo = true;
		}
		else{
			bo = false;
		}
		return bo;
	}
	
/**
* Rangeをコピーします。
* @param copy コピー元のRange
*/
	public void rangeCopy(Range copy){
		if(copy.isEmpty()){
			setMax(EMPTY_MAX);
			setMin(EMPTY_MIN);
			setEmpty(true);
			setHasMax(true);
			setHasMin(true);
		}
		else{
			setMax(copy.getMax());
			setMin(copy.getMin());
			setEmpty(copy.isEmpty());
			setHasMax(copy.hasMax());
			setHasMin(copy.hasMin());
		}
	}
	
/**
* Range 同士の積(AND)を返します。<BR>
* 2つの Range の重なっている区間を返します。
* 区間が重ならない場合もしくは、いずれか一方でも存在する区間なしである場合は、「存在する区間なし」を返します。
* @param ran 比較をする Range
* @return ANDを取った Range
*/
	public Range getAndRange(Range ran){
		double max;
		double min;
		boolean inMax;
		boolean inMin;
		
		//空集合のとき
		if(isEmpty() || ran.isEmpty()){
			return new Range();
		}
		
		//最大値設定
		if(getMax() > ran.getMax()){
			max = ran.getMax();
			inMax = ran.hasMax();
		}
		else if(getMax() < ran.getMax()){
			max = getMax();
			inMax = hasMax();
		}
		else if(hasMax() && ran.hasMax()){
			max = getMax();
			inMax = hasMax();
		}
		else{
			max = getMax();
			inMax = false;
		}
		
		//最小値設定
		if(getMin() < ran.getMin()){
			min = ran.getMin();
			inMin = ran.hasMin();
		}
		else if(getMin() > ran.getMin()){
			min = getMin();
			inMin = hasMin();
		}
		else if(hasMin() && ran.hasMin()){
			min = getMin();
			inMin = hasMin();
		}
		else{
			min = getMin();
			inMin = false;
		}
		
		//値設定
		if(max > min){
			return new Range(min, max, inMin, inMax);
		}
		else if(max == min){
			return new Range(min, max, true, true);
		}
		return new Range();
	}
	
/**
* 最大値を設定します。
* @param max 設定する最大値
*/
	private void setMax(double max){
		maxValue = max;
	}
	
/**
* 最小値を設定します。
* @param min 設定する最小値
*/
	private void setMin(double min){
		minValue = min;
	}
	
/**
* 存在する区間なしの論理値を設定します。
* @param emp 存在する区間なしならば true
*/
	private void setEmpty(boolean emp){
		empty = emp;
	}
	
/**
* 最大値は区間に含むか設定します。
* @param isInclude 区間に含む場合は true
*/
	private void setHasMax(boolean isInclude){
		includeMax = isInclude;
	}
	
/**
* 最小値は区間に含むか設定します。
* @param isInclude 区間に含む場合は true
*/
	private void setHasMin(boolean isInclude){
		includeMin = isInclude;
	}
	
/**
* Rangeを存在する区間なしで設定します。
*/
	private void createEmpty(){
		setMax(EMPTY_MAX);
		setMin(EMPTY_MIN);
		setEmpty(true);
		setHasMax(true);
		setHasMin(true);
	}
	
/**
* 最小値と最大値の範囲があっているかチェックします。
* @param min 最小値
* @param max 最大値
* @return 区間が存在しうる場合は true
*/
	private boolean isExistRange(double min, double max){
		return (min <= max);
	}
	
}