sascha@2695: package de.intevation.flys.artifacts.model;
sascha@2695: 
sascha@3059: import de.intevation.flys.artifacts.math.Linear;
sascha@3059: 
teichmann@4821: import de.intevation.flys.utils.DoubleUtil;
teichmann@4821: 
sascha@2695: import gnu.trove.TDoubleArrayList;
sascha@2695: 
sascha@2695: import java.io.Serializable;
sascha@2695: 
sascha@2785: import org.apache.log4j.Logger;
sascha@2785: 
sascha@2695: public class Parameters
sascha@2695: implements   Serializable
sascha@2695: {
sascha@2785:     private static Logger log = Logger.getLogger(Parameters.class);
sascha@2785: 
sascha@3217:     public interface Visitor {
sascha@3217: 
sascha@3217:         void visit(double [] row);
sascha@3217: 
sascha@3217:     } // interface Visitor
sascha@3217: 
sascha@3059:     public static final double EPSILON = 1e-4;
sascha@3059: 
sascha@2695:     protected String []           columnNames;
sascha@2695:     protected TDoubleArrayList [] columns;
sascha@2695: 
sascha@2695:     public Parameters() {
sascha@2695:     }
sascha@2695: 
sascha@2695:     public Parameters(String [] columnNames) {
sascha@2695:         if (columnNames == null || columnNames.length < 1) {
sascha@2695:             throw new IllegalArgumentException("columnNames too short.");
sascha@2695:         }
sascha@2695:         this.columnNames = columnNames;
sascha@2695:         columns = new TDoubleArrayList[columnNames.length];
sascha@2695:         for (int i = 0; i < columns.length; ++i) {
sascha@2695:             columns[i] = new TDoubleArrayList();
sascha@2695:         }
sascha@2695:     }
sascha@2695: 
sascha@2695:     public int columnIndex(String name) {
sascha@2695:         for (int i = 0; i < columnNames.length; ++i) {
ingo@2792:             if (columnNames[i].equals(name)) {
sascha@2695:                 return i;
sascha@2695:             }
sascha@2695:         }
sascha@3057:         if (log.isDebugEnabled()) {
sascha@3057:             log.debug("columnIndex: " + name + " not found in columnNames");
sascha@3057:         }
sascha@2695:         return -1;
sascha@2695:     }
sascha@2695: 
sascha@2729:     public int newRow() {
sascha@2695: 
sascha@2695:         int N = columns[0].size();
sascha@2695: 
sascha@2695:         for (int i = 0; i < columns.length; ++i) {
sascha@2695:             columns[i].add(Double.NaN);
sascha@2695:         }
sascha@2695: 
sascha@2729:         return N;
sascha@2695:     }
sascha@2695: 
sascha@3010:     public double get(int row, int index) {
sascha@3010:         return columns[index].getQuick(row);
sascha@2695:     }
sascha@2695: 
sascha@2695:     public double get(int i, String columnName) {
sascha@2695:         int index = columnIndex(columnName);
sascha@2695:         return index >= 0
sascha@2695:             ? columns[index].getQuick(i)
sascha@2695:             : Double.NaN;
sascha@2695:     }
sascha@2695: 
sascha@3010:     public void set(int row, int index, double value) {
sascha@3010:         columns[index].setQuick(row, value);
sascha@3010:     }
sascha@3010: 
sascha@2729:     public void set(int i, String columnName, double value) {
sascha@2729:         int idx = columnIndex(columnName);
sascha@2729:         if (idx >= 0) {
sascha@2785:             columns[idx].setQuick(i, value);
sascha@2729:         }
sascha@2729:     }
sascha@2729: 
sascha@3011:     public boolean set(int row, int [] indices, double [] values) {
sascha@3011:         boolean invalid = false;
sascha@3011:         for (int i = 0; i < indices.length; ++i) {
sascha@3011:             double v = values[i];
sascha@3011:             if (Double.isNaN(v)) {
sascha@3011:                 invalid = true;
sascha@3011:             }
sascha@3011:             else {
sascha@3011:                 columns[indices[i]].setQuick(row, v);
sascha@3011:             }
sascha@3011:         }
sascha@3011:         return invalid;
sascha@3011:     }
sascha@3011: 
sascha@3552:     public boolean set(int row, String [] names, double [] values) {
sascha@3552:         boolean success = true;
sascha@3552:         for (int i = 0; i < names.length; ++i) {
sascha@3552:             int idx = columnIndex(names[i]);
sascha@3552:             if (idx >= 0) {
sascha@3552:                 columns[idx].setQuick(row, values[i]);
sascha@3552:             }
sascha@3552:             else {
sascha@3552:                 success = false;
sascha@3552:             }
sascha@3552:         }
sascha@3552:         return success;
sascha@3552:     }
sascha@3552: 
sascha@2695:     public int size() {
sascha@2695:         return columns[0].size();
sascha@2695:     }
sascha@2695: 
sascha@2695:     public int getNumberColumns() {
sascha@2695:         return columnNames.length;
sascha@2695:     }
sascha@2695: 
sascha@2695:     public String [] getColumnNames() {
sascha@2695:         return columnNames;
sascha@2695:     }
sascha@2695: 
sascha@2695:     public void removeNaNs() {
teichmann@4821:         DoubleUtil.removeNaNs(columns);
sascha@2695:     }
sascha@2744: 
sascha@2744:     public int [] columnIndices(String [] columns) {
sascha@2744:         int [] indices = new int[columns.length];
sascha@2744:         for (int i = 0; i < columns.length; ++i) {
sascha@2744:             indices[i] = columnIndex(columns[i]);
sascha@2744:         }
sascha@2744:         return indices;
sascha@2744:     }
sascha@2744: 
sascha@3392:     public double getValue(int row, String column) {
sascha@3392:         int idx = columnIndex(column);
sascha@3392:         return idx >= 0
sascha@3392:             ? columns[idx].getQuick(row)
sascha@3392:             : Double.NaN;
sascha@3392:     }
sascha@3392: 
ingo@3105:     public double [] get(int row, String [] columns) {
ingo@3105:         return get(row, columns, new double[columns.length]);
ingo@3105:     }
ingo@3105: 
ingo@3105:     public double [] get(int row, String [] columns, double [] values) {
ingo@3105:         for (int i = 0; i < columns.length; ++i) {
ingo@3105:             int idx = columnIndex(columns[i]);
ingo@3105:             values[i] = idx < 0
ingo@3105:                 ? Double.NaN
ingo@3105:                 : this.columns[idx].getQuick(row);
ingo@3105:         }
ingo@3105: 
ingo@3105:         return values;
ingo@3105:     }
ingo@3105: 
sascha@2744:     public void get(int row, int [] columnIndices, double [] values) {
sascha@2744:         for (int i = 0; i < columnIndices.length; ++i) {
sascha@2744:             int index = columnIndices[i];
sascha@2744:             values[i] = index >= 0 && index < columns.length
sascha@2744:                 ? columns[index].getQuick(row)
sascha@2744:                 : Double.NaN;
sascha@2744:         }
sascha@2744:     }
sascha@3027: 
sascha@3027:     public int binarySearch(String columnName, double value) {
sascha@3027:         return binarySearch(columnIndex(columnName), value);
sascha@3027:     }
sascha@3027: 
christian@3056:     /**
christian@3056:      * Performes a binary search in the column identified by its
christian@3056:      * index.
christian@3056:      * @return Index of found element or negative insertion point (shifted by one)
christian@3056:      */
sascha@3027:     public int binarySearch(int columnIndex, double value) {
sascha@3027:         TDoubleArrayList column = columns[columnIndex];
sascha@3027:         return column.binarySearch(value);
sascha@3027:     }
sascha@3027: 
sascha@3027:     public int binarySearch(String columnName, double value, double epsilon) {
sascha@3027:         return binarySearch(columnIndex(columnName), value, epsilon);
sascha@3027:     }
sascha@3027: 
sascha@3027:     public int binarySearch(int columnIndex, double value, double epsilon) {
sascha@3057:         if (epsilon < 0d) epsilon = -epsilon;
sascha@3027:         double vl = value - epsilon;
sascha@3027:         double vh = value + epsilon;
sascha@3027: 
sascha@3027:         TDoubleArrayList column = columns[columnIndex];
sascha@3027:         int lo = 0, hi = column.size()-1;
raimund@3126:         while (hi >= lo) {
raimund@3126:             int mid = (lo + hi) >> 1;
sascha@3027:             double v = column.getQuick(mid);
raimund@3126:             if      (v < vl) lo = mid + 1;
raimund@3126:             else if (v > vh) hi = mid - 1;
sascha@3057:             else             return mid;
sascha@3027:         }
sascha@3027: 
sascha@3059:         return -(lo + 1);
sascha@3059:     }
sascha@3059: 
sascha@3059:     public double [] interpolate(int columnIndex, double key) {
sascha@3059:         return interpolate(columnIndex, key, new double[columns.length]);
sascha@3059:     }
sascha@3059: 
sascha@3059:     public double [] interpolate(String columnName, double key) {
sascha@3059:         return interpolate(
sascha@3059:             columnIndex(columnName), key, new double[columns.length]);
sascha@3059:     }
sascha@3059: 
sascha@3059:     public double [] interpolate(
sascha@3059:         String    columnName,
sascha@3076:         double    key,
sascha@3059:         double [] values
sascha@3059:     ) {
sascha@3059:         return interpolate(columnIndex(columnName), key, values);
sascha@3059:     }
sascha@3059: 
sascha@3061:     public double [] interpolate(
sascha@3076:         int       columnIndex,
sascha@3076:         double    key,
sascha@3061:         double [] values
sascha@3061:     ) {
sascha@3059:         int row = binarySearch(columnIndex, key, EPSILON);
sascha@3059: 
christian@3081:         if (row >= 0) { // direct hit
sascha@3059:             for (int i = 0; i < values.length; ++i) {
sascha@3059:                 values[i] = columns[i].getQuick(row);
sascha@3059:             }
sascha@3059:         }
sascha@3059:         else {
sascha@3059:             row = -row - 1;
sascha@3059:             if (row < 1 || row >= size()) {
sascha@3059:                 return null;
sascha@3059:             }
sascha@3059:             double v1 = columns[columnIndex].getQuick(row-1);
sascha@3059:             double v2 = columns[columnIndex].getQuick(row);
sascha@3059:             double factor = Linear.factor(key, v1, v2);
sascha@3059:             for (int i = 0; i < values.length; ++i) {
sascha@3059:                 values[i] = Linear.weight(
sascha@3059:                     factor,
sascha@3059:                     columns[i].getQuick(row-1),
sascha@3059:                     columns[i].getQuick(row));
sascha@3059:             }
sascha@3059:         }
sascha@3059:         return values;
sascha@3027:     }
sascha@3027: 
sascha@3061: 
sascha@3061:     public double [] interpolate(
sascha@3076:         String    keyName,
sascha@3076:         double    key,
sascha@3061:         String [] columnNames
sascha@3061:     ) {
sascha@3061:         int keyIndex = columnIndex(keyName);
sascha@3061:         return keyIndex < 0
sascha@3061:             ? null
sascha@3061:             : interpolate(keyIndex, key, columnNames);
sascha@3061:     }
sascha@3061: 
sascha@3061:     public double [] interpolate(
sascha@3061:         int       keyIndex,
sascha@3061:         double    key,
sascha@3061:         String [] columnNames
sascha@3061:     ) {
sascha@3061:         int row = binarySearch(keyIndex, key, EPSILON);
sascha@3061: 
sascha@3061:         if (row >= 0) { // direct match
sascha@3061:             double [] values = new double[columnNames.length];
sascha@3061:             for (int i = 0; i < values.length; ++i) {
sascha@3061:                 int ci = columnIndex(columnNames[i]);
sascha@3061:                 values[i] = ci < 0
sascha@3061:                     ? Double.NaN
sascha@3061:                     : columns[ci].getQuick(row);
sascha@3061:             }
sascha@3061:             return values;
sascha@3061:         }
sascha@3061: 
sascha@3061:         row = -row - 1;
christian@3081:         if (row < 1 || row >= size()) {
sascha@3082:             log.debug("interpolate: row is out of bounds");
sascha@3061:             return null;
sascha@3061:         }
sascha@3061: 
sascha@3061:         double v1 = columns[keyIndex].getQuick(row-1);
sascha@3061:         double v2 = columns[keyIndex].getQuick(row);
sascha@3061:         double factor = Linear.factor(key, v1, v2);
sascha@3061: 
sascha@3061:         double [] values = new double[columnNames.length];
sascha@3061: 
sascha@3061:         for (int i = 0; i < values.length; ++i) {
sascha@3061:             int ci = columnIndex(columnNames[i]);
sascha@3061:             values[i] = ci < 0
sascha@3061:                 ? Double.NaN
sascha@3061:                 : Linear.weight(
sascha@3061:                     factor,
sascha@3061:                     columns[ci].getQuick(row-1),
sascha@3061:                     columns[ci].getQuick(row));
sascha@3061:         }
sascha@3061: 
sascha@3061:         return values;
sascha@3061:     }
sascha@3061: 
sascha@3027:     public boolean isSorted(String columnName) {
sascha@3027:         return isSorted(columnIndex(columnName));
sascha@3027:     }
sascha@3027: 
sascha@3027:     public boolean isSorted(int columnIndex) {
sascha@3027:         TDoubleArrayList column = columns[columnIndex];
sascha@3027:         for (int i = 1, N = column.size(); i < N; ++i) {
sascha@3027:             if (column.getQuick(i-1) > column.getQuick(i)) {
sascha@3027:                 return false;
sascha@3027:             }
sascha@3027:         }
sascha@3027:         return true;
sascha@3027:     }
sascha@3217: 
sascha@3217:     public void visit(Visitor visitor) {
sascha@3217:         visit(visitor, new double[columns.length]);
sascha@3217:     }
sascha@3217: 
sascha@3217:     public void visit(Visitor visitor, double [] data) {
sascha@3217:         for (int i = 0, R = size(); i < R; ++i) {
sascha@3217:             for (int j = 0; j < data.length; ++j) {
sascha@3217:                 data[j] = columns[j].getQuick(i);
sascha@3217:             }
sascha@3217:             visitor.visit(data);
sascha@3217:         }
sascha@3217:     }
sascha@2695: }
sascha@2695: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :