/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2013 Ausenco Engineering Canada Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jaamsim.input;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public abstract class KeyedCurve<T> {
private class Key {
public double time;
public T val;
}
private ArrayList<Key> keys;
private boolean isSorted;
public KeyedCurve() {
keys = new ArrayList<>();
isSorted = true;
}
public void addKey(double time, T val) {
Key k = new Key();
k.time = time;
k.val = val;
keys.add(k);
isSorted = false;
}
public T getValAtTime(double time) {
if (!isSorted) {
sortKeys();
}
// Treat NaN as negative infinity, as it will at least return a valid number
if (Double.isNaN(time)) {
time = Double.NEGATIVE_INFINITY;
}
if (keys.size() == 0) {
return null;
}
if (keys.size() == 1) {
return keys.get(0).val;
}
// Are we ahead of the beginning?
if (time <= keys.get(0).time) {
return keys.get(0).val;
}
// Are we past the end?
if (time >= keys.get(keys.size()-1).time) {
return keys.get(keys.size()-1).val;
}
// Use a binary search to find the segment we want
int start = 0;
int end = keys.size() - 1;
while (end - start > 1) {
int pivot = (end + start)/2;
double pivotTime = keys.get(pivot).time;
if (pivotTime == time) {
return keys.get(pivot).val;
}
if (pivotTime > time) {
end = pivot;
} else {
start = pivot;
}
}
double startTime = keys.get(start).time;
double endTime = keys.get(end).time;
double ratio = (time - startTime) / (endTime - startTime);
return interpVal(keys.get(start).val, keys.get(end).val, ratio);
}
public boolean hasKeys() {
return keys.size() != 0;
}
protected abstract T interpVal(T val0, T val1, double ratio);
private void sortKeys() {
Collections.sort(keys, sorter);
isSorted = true;
}
private class KeySorter implements Comparator<Key>{
@Override
public int compare(Key arg0, Key arg1) {
return Double.compare(arg0.time, arg1.time);
}
}
private final KeySorter sorter = new KeySorter();
}