package net.sourceforge.gpstools.math;
/* gpsdings
* Copyright (C) 2007 Moritz Ringler
* $Id: PieceWiseFunction.java 441 2010-12-13 20:04:20Z ringler $
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import java.util.TreeSet;
import java.util.SortedSet;
import java.util.Comparator;
import org.apache.commons.math.FunctionEvaluationException;
import org.apache.commons.math.analysis.UnivariateRealFunction;
public class PieceWiseFunction implements UnivariateRealFunction{
transient SegmentFunction lastSegment;
private final static Comparator<SegmentFunction> funcSorter = new Comparator<SegmentFunction>(){
@Override
public int compare(SegmentFunction f1, SegmentFunction f2){
return f1.domain.compareTo(f2.domain);
}
};
protected SortedSet<SegmentFunction> elements = new TreeSet<SegmentFunction>(funcSorter);
public PieceWiseFunction(){
// explicit default constructor.
}
/** @return a sorted array of the intervals that jointly form the domain
* of this function */
public synchronized ClosedInterval[] getDomain(){
final int n = elements.size();
ClosedInterval[] result = new ClosedInterval[n];
int i = 0;
for(SegmentFunction f : elements){
result[i++] = f.domain;
}
return result;
}
public synchronized boolean addSegmentFunction(SegmentFunction f){
for(SegmentFunction other : elements){
if(f.domain.overlaps(other.domain) && ! f.equals(other)){
throw new IllegalArgumentException("The domain of this SegmentFunction overlaps with that of another one.");
}
}
return elements.add(f);
}
public synchronized boolean addSegmentFunction(double xmin, double xmax, UnivariateRealFunction f){
return addSegmentFunction(new SegmentFunction(xmin, xmax, f));
}
public UnivariateRealFunction getSegmentFor(double x) throws FunctionEvaluationException {
// try the segment that contained the last x-value first
if(lastSegment != null && lastSegment.domain.contains(x)){
return lastSegment.func;
}
for(SegmentFunction segment : elements){
if(segment.domain.contains(x)){
lastSegment = segment;
return segment.func;
}
}
return null;
}
@Override
public double value(double x) throws FunctionEvaluationException {
UnivariateRealFunction seg = getSegmentFor(x);
if(seg == null){
throw new FunctionEvaluationException(x, "Argument outside domain: " + x);
}
return seg.value(x);
}
/* class segment function */
public static class SegmentFunction{
public final ClosedInterval domain;
public final UnivariateRealFunction func;
public SegmentFunction(ClosedInterval domain, UnivariateRealFunction func){
this.domain = domain;
this.func = func;
}
public SegmentFunction(double xmin, double xmax, UnivariateRealFunction func){
this.domain = new ClosedInterval(xmin, xmax);
this.func = func;
}
@Override
public boolean equals(Object o){
if(this == o){
return true;
}
if(o instanceof SegmentFunction){
SegmentFunction other = (SegmentFunction) o;
return
func.equals(other.func) &&
domain.equals(other.domain);
}
return false;
}
@Override
public int hashCode(){
int result = 17;
result = 37 * result + domain.hashCode();
result = 37 * result + func.hashCode();
return result;
}
}
public final static class ClosedInterval implements Comparable<ClosedInterval>{
public final double xmin;
public final double xmax;
public ClosedInterval(double xmin, double xmax){
if (xmax < xmin){
throw new IllegalArgumentException("xmax must be greater than or equal to xmin.");
}
this.xmin = xmin;
this.xmax = xmax;
}
@Override
public boolean equals(Object o){
if(this == o){
return true;
}
if(o instanceof ClosedInterval){
ClosedInterval other = (ClosedInterval) o;
return
this.xmin == other.xmin &&
this.xmax == other.xmax;
}
return false;
}
@Override
public int hashCode(){
int result = 17;
long d = Double.doubleToLongBits(xmin);
result = 37 * result + (int)(d ^ (d >>> 32));
d = Double.doubleToLongBits(xmax);
result = 37 * result + (int)(d ^ (d >>> 32));
return result;
}
@Override
public int compareTo(ClosedInterval other){
int result = Double.compare(xmin, other.xmin);
return (result == 0)
? Double.compare(xmax, other.xmax)
: result;
}
public boolean overlaps(ClosedInterval other){
boolean isless = (compareTo(other) <= 0);
ClosedInterval a = isless? this : other;
ClosedInterval b = isless? other : this;
return a.xmax > b.xmin;
//return a.xmax >= b.xmin;
}
public boolean contains(double x){
return x >= xmin && x <= xmax;
}
}
}