code: /*******************************************************************************
   * Copyright (c) 2013 Attila Kovacs <attila[AT]sigmyne.com>.
   * All rights reserved. 
   * 
   * This file is part of crush.
   * 
   *     crush 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.
   * 
   *     crush 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 crush.  If not, see <http://www.gnu.org/licenses/>.
   * 
   * Contributors:
   *     Attila Kovacs <attila[AT]sigmyne.com> - initial API and implementation
   ******************************************************************************/
  package crush.instrument;
  
  import java.io.*;
  import java.util.*;
  
  import jnum.Util;
  import jnum.math.Vector2D;
  
  
  public class RCPFile {
  	Hashtable<Integer, Entry> table = new Hashtable<>();
  	
  	public RCPFile(String fileName) throws IOException {
  		Vector2D offset = new Vector2D();
  		
  		if(fileName.contains("@")) {
  			int i = fileName.lastIndexOf('@');
  			offset.parse(fileName.substring(i+1));
  			System.err.print("@" + offset + " ");
  			fileName = fileName.substring(0, i);
  		}
  		
  		read(fileName);
  		subtract(offset);
  	}
  	
  	public void read(String fileName) throws IOException {
  		table.clear();
  		
  		System.err.print("Reading " + fileName + "... ");
  		
  		try(BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)))) {
  		    String line = null;
  
  		    while((line = in.readLine()) != null) if(line.length() > 0) if(line.charAt(0) != '#') {
  		        Entry entry = new Entry();
  		        try { 
  		            entry.parse(line); 
  		            table.put(entry.index, entry);
  		        }
  		        catch(IllegalArgumentException e) {}
  		    }
  		    in.close();
  		}
  
  		System.err.println(table.size() + " entries found.");
  	}
  
  	public void subtract(Vector2D offset) {
  		for(Entry entry : table.values()) {
  			entry.x -= offset.x();
  			entry.y -= offset.y();			
  		}
  	}
  	
  	public void print() {
  		Set<Integer> keys = table.keySet();
  		Vector<Integer> list = new Vector<>(keys); 
  		Collections.sort(list);
  		for(int i : list) System.out.println(table.get(i));	
  	}
  	
  	public void averageWith(RCPFile other) {
  		for(int i : other.table.keySet()) {
  			if(table.containsKey(i)) table.get(i).averageWith(other.table.get(i));
  			else table.put(i, other.table.get(i));			
  		}
  	}
  	
  	public class Entry {
  		int index;
  		double x = Double.NaN;
  		double y = Double.NaN;
  		double sourceGain = Double.NaN;
  		double skyGain = Double.NaN;	
  		int n = 0;
  		
  		public void parse(String line) {
  			StringTokenizer tokens = new StringTokenizer(line);
  			if(tokens.countTokens() < 3) throw new IllegalArgumentException("not enough columns");
  			
  			n = 1;
  			index = Integer.parseInt(tokens.nextToken());
  			x = Double.parseDouble(tokens.nextToken());
  			y = Double.parseDouble(tokens.nextToken());
  			
  			if(tokens.hasMoreTokens()) sourceGain = Double.parseDouble(tokens.nextToken());
  			if(tokens.hasMoreTokens()) skyGain = Double.parseDouble(tokens.nextToken());
  		}
  		
  		public void averageWith(Entry other) {
  			if(index != other.index) throw new IllegalStateException("Mismatched indexes");
  			double f = (double) n / (n + other.n);
  			
  			x = f * x + (1.0 - f) * other.x;
  			y = f * y + (1.0 - f) * other.y;
  			sourceGain = f * sourceGain + (1.0 - f) * other.sourceGain;
  			skyGain = f * skyGain + (1.0 - f) * other.skyGain;
  			
  			n += other.n;
  		}
  		
  		@Override
  		public String toString() {
  			String line =  index + "\t" + Util.f2.format(x) + "\t" + Util.f2.format(y);
  			if(!Double.isNaN(sourceGain)) {
  				line += "\t" + Util.f3.format(sourceGain);
  				if(!Double.isNaN(skyGain)) line += "\t" + Util.f3.format(skyGain);
  			}
   			return line;
  		}
  	}
  }
  