package yacs.domain;

import net.sourceforge.interval.ia_math.*;

/**
 * <p>Klasse zur Repräsentation von einzelnen "Elementen" für reellwertige
 * Intervall-Domänen. Mit einem Element ist in diesem Fall ein kontinuierliches
 * (Teil-)Intervall gemeint. Diese Klasse stützt sich auf die in der Bibliothek
 * <a href="http://interval.sourceforge.net/interval/java/ia_math/README.html">IAMath</a>
 * von <a href="http://www.cs.brandeis.edu/~tim/">Timothy J. Hickey</a>
 * implementierte Intervallarithmetik.</p>
 *
 * <p>Copyright (C) 2005  Wolfgang Runte
 * <br><br>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <br><br>
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * <br><br>
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA</p>
 *
 * @author Wolfgang Runte (woru@tzi.org)
 * @version YACS 0.1.1
 */
public class IntervalDomainElement extends DomainElement {

// ********** private attributs **********

  /** Der Wert dieses Elements. */
  private RealInterval value;

// ********** public attributs **********

// ********** private methods **********

// ********** public methods **********

  /**
   * Konstruktor -- Benötigt ein <code>RealInterval</code>-Objekt zur
   * Instantiierung.
   */
  public IntervalDomainElement(RealInterval value) {
    this.value = value;
  }

  /**
   * Konstruktor -- Aus Gründen der Vereinfachung können zur Instantiierung auch
   * zwei <code>Double</code>-Objekte als untere und obere Schranke des
   * Intervalls angegeben werden.
   */
  public IntervalDomainElement(Double lo, Double hi) {
    this(new RealInterval(lo.doubleValue(), hi.doubleValue()));
  }

  /**
   * Konstruktor -- Aus Gründen der Vereinfachung können zur Instantiierung auch
   * lediglich zwei <code>double</code>-Werte als untere und obere Schranke des
   * Intervalls angegeben werden.
   */
  public IntervalDomainElement(double lo, double hi) {
    this(new RealInterval(lo, hi));
  }

  /**
   * Liefert die untere Schranke dieses Intervalls.
   * @return double
   */
  public double getLowerBound() {
    return this.value.lo();
  }

  /**
   * Liefert die obere Schranke dieses Intervalls.
   * @return double
   */
  public double getUpperBound() {
    return this.value.hi();
  }

  /**
   * Setzt die untere Schranke dieses Intervalls.
   * @param lowerBound double
   */
  public void setLowerBound(double lowerBound) {
    this.value = new RealInterval(lowerBound, this.getUpperBound());
  }

  /**
   * Setzt die obere Schranke dieses Intervalls.
   * @param upperBound double
   */
  public void setUpperBound(double upperBound) {
    this.value = new RealInterval(this.getLowerBound(), upperBound);
  }

  public Object getValue() {
    return this.value;
  }

  public DomainElement plus(DomainElement param) {
    return new IntervalDomainElement(IAMath.add(this.value, (RealInterval)param.getValue()));
  }

  public DomainElement minus(DomainElement param) {
    return new IntervalDomainElement(IAMath.sub(this.value, (RealInterval)param.getValue()));
  }

  public DomainElement times(DomainElement param) {
    return new IntervalDomainElement(IAMath.mul(this.value, (RealInterval)param.getValue()));
  }

  public DomainElement divide(DomainElement param) {
    return new IntervalDomainElement(IAMath.div(this.value, (RealInterval)param.getValue()));
  }

  public DomainElement uminus() {
    return new IntervalDomainElement(IAMath.uminus(this.value));
  }

  /**
   * Liefert <code>true</code>, wenn die obere und untere Schranke des
   * Intervalls jeweils identisch mit der des übergebenen Intervalls ist,
   * <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public boolean equals(DomainElement param) {
    //return value.equals((RealInterval)param.getValue());
    return (this.value.lo() == ((RealInterval)param.getValue()).lo()) &&
           (this.value.hi() == ((RealInterval)param.getValue()).hi());
  }

  /**
   * Liefert <code>true</code>, wenn die obere oder untere Schranke des
   * Intervalls jeweils nicht identisch mit der des übergebenen Intervall ist,
   * <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public boolean notEquals(DomainElement param) {
    //return !value.equals((RealInterval)param.getValue());
    return (this.value.lo() != ((RealInterval)param.getValue()).lo()) ||
           (this.value.hi() != ((RealInterval)param.getValue()).hi());
  }

  /**
   * Liefert <code>true</code>, wenn sämtliche Werte des Intervalls größer als
   * die des übergebenen Intervalls sind, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public boolean greaterAs(DomainElement param) {
    return (this.value.lo() > ((RealInterval)param.getValue()).hi()) &&
           (this.value.hi() > ((RealInterval)param.getValue()).hi());
  }

  /**
   * Liefert <code>true</code>, wenn sämtliche Werte des Intervalls kleiner als
   * die des übergebenen Intervalls sind, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public boolean lowerAs(DomainElement param) {
    return (this.value.lo() < ((RealInterval)param.getValue()).lo()) &&
           (this.value.hi() < ((RealInterval)param.getValue()).lo());
  }

  /**
   * Liefert <code>true</code>, wenn die Werte des Intervalls größer/gleich dem
   * übergebenen Intervall sind, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public boolean greaterEqualAs(DomainElement param) {
    return (this.value.lo() >= ((RealInterval)param.getValue()).lo()) &&
           (this.value.hi() >= ((RealInterval)param.getValue()).hi());
  }

  /**
   * Liefert <code>true</code>, wenn die Werte des Intervalls kleiner/gleich dem
   * übergebenen Intervall sind, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public boolean lowerEqualAs(DomainElement param) {
    return (this.value.lo() <= ((RealInterval)param.getValue()).lo()) &&
           (this.value.hi() <= ((RealInterval)param.getValue()).hi());
  }

  /**
   * Liefert <code>true</code>, wenn alle Werte des übergebenen Intervalls in
   * diesem Intervall vollständig enthalten sind, <code>false</code> wenn nicht.
   * @param param DomainElement
   * @return boolean
   */
  public boolean contains(DomainElement param) {
    return (this.value.lo() <= ((RealInterval)param.getValue()).lo()) &&
           (this.value.hi() >= ((RealInterval)param.getValue()).hi());
  }

  public String toString() {
    return this.value.toString();
  }

/*
  public static void main(String[] args) {
    //
    // Einige Tests mit der IAMath-Bibliothek:
    //
    RealInterval a = new RealInterval(1,2);
    RealInterval b = new RealInterval(3,4);
    RealInterval c = new RealInterval(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);

//    RealInterval result = IAMath.add(a, b);

    System.out.println("Intervall 'a': " + a);
    System.out.println("Intervall 'b': " + b);
//    System.out.println("Ergebnis 'a + b': " + result);

    System.out.println("Test eq(): " + IANarrow.narrow_eq(c,a,b));//ACHTUNG, Werte von 'a' verändert!
    //Liefert 'true' auch wenn es nur Überschneidungen gibt.
//    System.out.println("Test ne(): " + IANarrow.narrow_ne(c,a,b));//
    //Liefert (fast) immer 'true'?! ((x.lo < x.hi) || (y.lo < y.hi) || (x.lo != y.lo))
//    System.out.println("Test gt(): " + IANarrow.narrow_gt(c,a,b));//ACHTUNG, beide verändert!
    //Liefert bereits 'true' wenn es Elemente in 'a' gibt, die größer sind als 'b.lo'.
//    System.out.println("Test lt(): " + IANarrow.narrow_lt(c,a,b));
    //Liefert bereits 'true' wenn es Elemente in 'a' gibt, die kleiner sind als 'b.hi'.
//    System.out.println("Test ge(): " + IANarrow.narrow_ge(c,a,b));//ACHTUNG, beide verändert!
    //Liefert auch noch 'true' wenn 'a.hi = b.lo'.
//    System.out.println("Test le(): " + IANarrow.narrow_le(c,a,b));
    //Liefert auch noch 'true' wenn 'a.lo = b.hi'.

    System.out.println("Intervall 'a': " + a);
    System.out.println("'a.lo()': " + a.lo() + " - " + "'a.hi()': " + a.hi());
    System.out.println("Intervall 'b': " + b);
    System.out.println("'b.lo()': " + b.lo() + " - " + "'b.hi()': " + b.hi());

    System.out.println("Intervall 'c': " + c);
    System.out.println();

    //
    // Einige Tests mit den Operatoren von "IntervalDomainElement":
    //
    DomainElement test1 = new IntervalDomainElement(1,2);
    DomainElement test2 = new IntervalDomainElement(3,4);

    System.out.println("Test 1: " + test1.plus(test2));
    System.out.println("Test 2: " + test1.minus(test2));
    System.out.println("Test 3: " + test1.times(test2));
    System.out.println("Test 4: " + test1.divide(test2));

    System.out.println("Test 5: " + test1.equals(test2));
    System.out.println("Test 6: " + test1.notEquals(test2));
    System.out.println("Test 7: " + test1.greaterAs(test2));
    System.out.println("Test 8: " + test1.lowerAs(test2));
    System.out.println("Test 9: " + test1.greaterEqualAs(test2));
    System.out.println("Test 10: " + test1.lowerEqualAs(test2));

    System.out.println("Test 11: " + test1 + " - " + test2);
  }
*/

}
