1     package jgi;
2 
3 import java.io.PrintStream;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.Collections;
7 import java.util.LinkedHashMap;
8 import java.util.Map.Entry;
9 
10 import fileIO.ByteFile;
11 import fileIO.ByteStreamWriter;
12 import fileIO.FileFormat;
13 import fileIO.ReadWrite;
14 import json.JsonObject;
15 import json.JsonParser;
16 import server.ServerTools;
17 import shared.Parse;
18 import shared.Parser;
19 import shared.PreParser;
20 import shared.Shared;
21 import shared.Timer;
22 import shared.Tools;
23 import structures.ByteBuilder;
24 
25 /**
26  * @author Brian Bushnell
27  * @date May 9, 2016
28  *
29  */
30 public class GatherKapaStats {
31 
32 	/*--------------------------------------------------------------*/
33 	/*----------------        Initialization        ----------------*/
34 	/*--------------------------------------------------------------*/
35 
36 	/**
37 	 * Code entrance from the command line.
38 	 * @param args Command line arguments
39 	 */
main(String[] args)40 	public static void main(String[] args){
41 		//Start a timer immediately upon code entrance.
42 		Timer t=new Timer();
43 
44 		//Create an instance of this class
45 		GatherKapaStats x=new GatherKapaStats(args);
46 
47 		//Run the object
48 		x.process(t);
49 
50 		//Close the print stream if it was redirected
51 		Shared.closeStream(x.outstream);
52 	}
53 
54 	/**
55 	 * Constructor.
56 	 * @param args Command line arguments
57 	 */
GatherKapaStats(String[] args)58 	public GatherKapaStats(String[] args){
59 
60 		{//Preparse block for help, config files, and outstream
61 			PreParser pp=new PreParser(args, getClass(), false);
62 			args=pp.args;
63 			outstream=pp.outstream;
64 		}
65 
66 		//Set shared static variables prior to parsing
67 		ReadWrite.USE_PIGZ=ReadWrite.USE_UNPIGZ=true;
68 		ReadWrite.MAX_ZIP_THREADS=Shared.threads();
69 
70 		{//Parse the arguments
71 			final Parser parser=parse(args);
72 			overwrite=parser.overwrite;
73 			append=parser.append;
74 
75 			in1=parser.in1;
76 
77 			out1=parser.out1;
78 		}
79 
80 		fixExtensions(); //Add or remove .gz or .bz2 as needed
81 		checkFileExistence(); //Ensure files can be read and written
82 		checkStatics(); //Adjust file-related static fields as needed for this program
83 
84 		ffout1=FileFormat.testOutput(out1, FileFormat.TXT, null, true, overwrite, append, false);
85 		ffin1=FileFormat.testInput(in1, FileFormat.TXT, null, true, true);
86 	}
87 
88 	/*--------------------------------------------------------------*/
89 	/*----------------    Initialization Helpers    ----------------*/
90 	/*--------------------------------------------------------------*/
91 
92 	/** Parse arguments from the command line */
parse(String[] args)93 	private Parser parse(String[] args){
94 
95 		Parser parser=new Parser();
96 		for(int i=0; i<args.length; i++){
97 			String arg=args[i];
98 			String[] split=arg.split("=");
99 			String a=split[0].toLowerCase();
100 			String b=split.length>1 ? split[1] : null;
101 			if(b!=null && b.equalsIgnoreCase("null")){b=null;}
102 
103 			if(a.equals("lines")){
104 				maxLines=Long.parseLong(b);
105 				if(maxLines<0){maxLines=Long.MAX_VALUE;}
106 			}else if(a.equals("verbose")){
107 				verbose=Parse.parseBoolean(b);
108 //				ByteFile1.verbose=verbose;
109 //				ByteFile2.verbose=verbose;
110 //				ReadWrite.verbose=verbose;
111 			}else if(a.equals("raw") || a.equals("printraw")){
112 				printRaw=Parse.parseBoolean(b);
113 			}else if(parser.parse(arg, a, b)){
114 				//do nothing
115 			}else{
116 				outstream.println("Unknown parameter "+args[i]);
117 				assert(false) : "Unknown parameter "+args[i];
118 				//				throw new RuntimeException("Unknown parameter "+args[i]);
119 			}
120 		}
121 
122 		return parser;
123 	}
124 
125 	/** Add or remove .gz or .bz2 as needed */
fixExtensions()126 	private void fixExtensions(){
127 		in1=Tools.fixExtension(in1);
128 		if(in1==null){throw new RuntimeException("Error - at least one input file is required.");}
129 	}
130 
131 	/** Ensure files can be read and written */
checkFileExistence()132 	private void checkFileExistence(){
133 		//Ensure output files can be written
134 		if(!Tools.testOutputFiles(overwrite, append, false, out1)){
135 			outstream.println((out1==null)+", "+out1);
136 			throw new RuntimeException("\n\noverwrite="+overwrite+"; Can't write to output file "+out1+"\n");
137 		}
138 
139 		//Ensure input files can be read
140 		if(!Tools.testInputFiles(false, true, in1)){
141 			throw new RuntimeException("\nCan't read some input files.\n");
142 		}
143 
144 		//Ensure that no file was specified multiple times
145 		if(!Tools.testForDuplicateFiles(true, in1, out1)){
146 			throw new RuntimeException("\nSome file names were specified multiple times.\n");
147 		}
148 	}
149 
150 	/** Adjust file-related static fields as needed for this program */
checkStatics()151 	private static void checkStatics(){
152 		if(!ByteFile.FORCE_MODE_BF2){
153 			ByteFile.FORCE_MODE_BF2=false;
154 			ByteFile.FORCE_MODE_BF1=true;
155 		}
156 	}
157 
158 	/*--------------------------------------------------------------*/
159 	/*----------------         Outer Methods        ----------------*/
160 	/*--------------------------------------------------------------*/
161 
process(Timer t)162 	void process(Timer t){
163 
164 		ByteFile bf=ByteFile.makeByteFile(ffin1);
165 		ArrayList<Plate> plates=loadPlates(bf);
166 		errorState|=bf.close();
167 
168 		analyzePlates(plates);
169 
170 		ByteStreamWriter bsw=makeBSW(ffout1);
171 		if(bsw!=null){
172 			if(printRaw){
173 				printRawResults(bsw);
174 			}else{
175 				printResults(bsw);
176 			}
177 			errorState|=bsw.poisonAndWait();
178 		}
179 
180 		t.stop();
181 
182 		outstream.println(Tools.timeLinesBytesProcessed(t, linesProcessed, bytesProcessed, 8));
183 
184 		outstream.println();
185 		outstream.println("Lines Out:         \t"+linesOut);
186 //		outstream.println("Valid Lines:       \t"+linesOut);
187 //		outstream.println("Invalid Lines:     \t"+(linesProcessed-linesOut));
188 
189 		if(errorState){
190 			throw new RuntimeException(getClass().getName()+" terminated in an error state; the output may be corrupt.");
191 		}
192 	}
193 
194 	/*--------------------------------------------------------------*/
195 	/*----------------         Inner Methods        ----------------*/
196 	/*--------------------------------------------------------------*/
197 
loadPlates(ByteFile bf)198 	private ArrayList<Plate> loadPlates(ByteFile bf){
199 
200 		ArrayList<Plate> plates=new ArrayList<Plate>();
201 		byte[] line=bf.nextLine();
202 
203 		while(line!=null){
204 			if(line.length>0){
205 				if(maxLines>0 && linesProcessed>=maxLines){break;}
206 				linesProcessed++;
207 				bytesProcessed+=(line.length+1);
208 
209 				final boolean valid=(line[0]!='#');
210 
211 				if(valid){
212 					String[] split=new String(line).split("\t");
213 					assert(split.length>=1) : Arrays.toString(split);
214 					String name=split[0];
215 					String lot=(split.length>1 ? split[1] : null);
216 					Plate plate=new Plate(name, lot);
217 					plate.fillFromWeb();
218 					plates.add(plate);
219 					plateMap.put(name, plate);
220 				}
221 			}
222 			line=bf.nextLine();
223 		}
224 		return plates;
225 	}
226 
analyzePlates(ArrayList<Plate> plates)227 	private void analyzePlates(ArrayList<Plate> plates){
228 		for(Plate p : plates){
229 			for(Well w : p.wells){
230 				long kapaReads=w.correctKapaReads+w.incorrectKapaReads;
231 				if(kapaReads>0){
232 					final double mult=1000000.0/kapaReads;
233 					TagData td=tagMap.get(w.correctKapaTag);
234 					if(td==null){
235 						td=new TagData(w.correctKapaTag, w.name);
236 						tagMap.put(w.correctKapaTag, td);
237 					}
238 					td.timesSeen++;
239 					for(Entry<String, KapaEntry> e : w.kapaMap.entrySet()){
240 						KapaEntry ke=e.getValue();
241 						double ppmk=ke.reads*mult;
242 						td.add(ke.tagName, ppmk, p.name);
243 					}
244 				}
245 			}
246 		}
247 	}
248 
printResults(ByteStreamWriter bsw)249 	private void printResults(ByteStreamWriter bsw){
250 		ArrayList<TagData> list=new ArrayList<TagData>();
251 		list.addAll(tagMap.values());
252 		Collections.sort(list);
253 		ByteBuilder bb=new ByteBuilder();
254 		bb.append("#Tag\tOther\tMin\t25%\t50%\t75%\tMax\tAvg\tStdev\tObserved\tTotal\tFraction\n");
255 		for(TagData td : list){
256 			ArrayList<String> keys=new ArrayList<String>();
257 			keys.addAll(td.ppmMap.keySet());
258 			Collections.sort(keys);
259 			final int len=td.timesSeen;
260 			for(String key : keys){
261 				double[] ppmk=td.getPpmArray(key, true);
262 //				if(ppmk.length<seen){
263 //					ppmk=Arrays.copyOf(ppmk, newLength)
264 //				}
265 				assert(len==ppmk.length);
266 				int count=0;
267 				for(double d : ppmk){
268 					if(d>0){count++;}
269 				}
270 				double min=ppmk[0];
271 				double max=ppmk[len-1];
272 				double p25=ppmk[(int)Math.round((len-1)*.25)];
273 				double p50=ppmk[(int)Math.round((len-1)*.50)];
274 				double p75=ppmk[(int)Math.round((len-1)*.75)];
275 				double avg=Tools.sum(ppmk)/len;
276 				double stdev=Tools.standardDeviation(ppmk);
277 				bb.append(td.name).append('\t');
278 				bb.append(key).append('\t');
279 				bb.append(min, 2).append('\t');
280 				bb.append(p25, 2).append('\t');
281 				bb.append(p50, 2).append('\t');
282 				bb.append(p75, 2).append('\t');
283 				bb.append(max, 2).append('\t');
284 				bb.append(avg, 2).append('\t');
285 				bb.append(stdev, 2).append('\t');
286 				bb.append(count).append('\t');
287 				bb.append(len).append('\t');
288 				bb.append(count/(double)len, 4).append('\n');
289 				bsw.print(bb);
290 				linesOut++;
291 				bytesOut+=bb.length;
292 				bb.clear();
293 //				bsw.println(Arrays.toString(ppmk));
294 			}
295 		}
296 		if(!bb.isEmpty()){
297 			linesOut++;
298 			bytesOut+=bb.length;
299 			bsw.print(bb);
300 		}
301 	}
302 
printRawResults0(ByteStreamWriter bsw)303 	private void printRawResults0(ByteStreamWriter bsw){
304 		ArrayList<TagData> list=new ArrayList<TagData>();
305 		list.addAll(tagMap.values());
306 		Collections.sort(list);
307 		ByteBuilder bb=new ByteBuilder();
308 		bb.append("#Tag\tOther\tTotal\tPPM,...\n");
309 		for(TagData td : list){
310 			ArrayList<String> keys=new ArrayList<String>();
311 			keys.addAll(td.ppmMap.keySet());
312 			Collections.sort(keys);
313 			final int len=td.timesSeen;
314 			for(String key : keys){
315 				double[] ppmk=td.getPpmArray(key, true);
316 //				if(ppmk.length<seen){
317 //					ppmk=Arrays.copyOf(ppmk, newLength)
318 //				}
319 				assert(len==ppmk.length);
320 				bb.append(td.name).append('\t');
321 				bb.append(key).append('\t');
322 				bb.append(len).append('\t');
323 				String comma="";
324 				for(double d : ppmk){
325 					bb.append(comma);
326 					bb.append(d, 2);
327 					comma=",";
328 				}
329 				bb.append('\n');
330 
331 				bsw.print(bb);
332 				linesOut++;
333 				bytesOut+=bb.length;
334 				bb.clear();
335 //				bsw.println(Arrays.toString(ppmk));
336 			}
337 		}
338 		if(!bb.isEmpty()){
339 			linesOut++;
340 			bytesOut+=bb.length;
341 			bsw.print(bb);
342 		}
343 	}
344 
printRawResults(ByteStreamWriter bsw)345 	private void printRawResults(ByteStreamWriter bsw){
346 		ArrayList<TagData> list=new ArrayList<TagData>();
347 		list.addAll(tagMap.values());
348 		Collections.sort(list);
349 		ByteBuilder bb=new ByteBuilder();
350 		bb.append("#Plate\tSinkWell\tSinkCorrectTag\tSinkReads\tSinkCorrectKapaReads\tSinkTotalKapaReads\t"
351 				+ "SourceWell\tMeasuredTag\tSourceReads\tSourceCorrectKapaReads\tSourceKapaReadsInSink\t"
352 				+ "KPPM (SourceKapa/SinkKapa)\tGReads (InferredContamGenomicReads)\t"
353 				+ "GPPM (InferredContamGenomicPPM)\n");
354 		for(TagData td : list){
355 			ArrayList<String> keys=new ArrayList<String>();
356 			keys.addAll(td.ppmMap.keySet());
357 			Collections.sort(keys);
358 			final int len=td.timesSeen;
359 			for(String key : keys){
360 				double[] ppmk=td.getPpmArray(key, false);
361 				String[] plateNames=td.getPlateNameArray(key, false);
362 //				if(ppmk.length<seen){
363 //					ppmk=Arrays.copyOf(ppmk, newLength)
364 //				}
365 				assert(len==ppmk.length);
366 				for(int i=0; i<ppmk.length; i++){
367 					double d=ppmk[i];
368 					String plateName=plateNames[i];
369 					if(plateName!=null){
370 						Plate plate=plateMap.get(plateName);
371 						Well sink=plate.tagToCorrectWellMap.get(td.name);
372 						Well source=plate.tagToCorrectWellMap.get(key);
373 						if(source==null){source=dummy;}
374 						KapaEntry keSource=sink.kapaMap.get(key);
375 						long contamReads=keSource.reads;
376 						double greads=contamReads*(source.reads/(double)source.correctKapaReads);
377 						double gppm=1000000*greads/(double)sink.reads;
378 
379 						bb.append(plateName).tab();
380 						bb.append(td.wellName).tab();
381 						bb.append(td.name).tab();
382 						bb.append(sink.reads).tab();
383 						bb.append(sink.correctKapaReads).tab();
384 						bb.append(sink.correctKapaReads+sink.incorrectKapaReads).tab();
385 						bb.append(source.name).tab();
386 						bb.append(key).tab();
387 						bb.append(source.reads).tab();
388 						bb.append(source.correctKapaReads).tab();
389 						bb.append(contamReads).tab();
390 						bb.append(d, 2).tab();
391 						bb.append(greads, 2).tab();
392 						bb.append(gppm, 2).nl();
393 					}
394 				}
395 
396 				bsw.print(bb);
397 				linesOut++;
398 				bytesOut+=bb.length;
399 				bb.clear();
400 //				bsw.println(Arrays.toString(ppmk));
401 			}
402 		}
403 		if(!bb.isEmpty()){
404 			linesOut++;
405 			bytesOut+=bb.length;
406 			bsw.print(bb);
407 		}
408 	}
409 
makeBSW(FileFormat ff)410 	private static ByteStreamWriter makeBSW(FileFormat ff){
411 		if(ff==null){return null;}
412 		ByteStreamWriter bsw=new ByteStreamWriter(ff);
413 		bsw.start();
414 		return bsw;
415 	}
416 
417 	/*--------------------------------------------------------------*/
418 	/*----------------        Nested Classes        ----------------*/
419 	/*--------------------------------------------------------------*/
420 
421 	class Plate{
422 
Plate(String name_, String lot_)423 		public Plate(String name_, String lot_){
424 			name=name_;
425 			lot=lot_;
426 		}
427 
fillFromWeb()428 		void fillFromWeb(){
429 			JsonObject data=grabData();
430 			int size=data.jmapSize();
431 			wells=new ArrayList<Well>(size);
432 			if(size<1){
433 				outstream.println("No Kapa for plate "+name);
434 				return;
435 			}
436 			for(Entry<String, JsonObject> e : data.jmap.entrySet()){
437 				String key=e.getKey();
438 				JsonObject jo=e.getValue();
439 				Well well=new Well(key, jo, name);
440 				wells.add(well);
441 				tagToCorrectWellMap.put(well.correctKapaTag, well);
442 				if(verbose && well.name.contentEquals("B1")){
443 					System.err.println(well);
444 				}
445 			}
446 		}
447 
grabData()448 		JsonObject grabData(){
449 			String address=addressPrefix+name+addressSuffix;
450 //			System.err.println("Reading "+address);
451 			ByteBuilder message=ServerTools.readPage(address, true);
452 			assert(message!=null && message.length()>0) : "No data from address "+address;
453 
454 			jp.set(message.toBytes());
455 			JsonObject jo=jp.parseJsonObject();
456 			assert(jo!=null && jo.jmapSize()==1 && jo.omapSize()==0) : jo.toString();
457 
458 			JsonObject data=jo.getJson("data");
459 			assert(data!=null) : jo.toString();
460 //			assert(data.jmapSize()>0 /*&& data.smapSize()==0*/) : data.toString()+"\n\n"+data.smap+"\n\n"+data.jmapSize(); //These assertions are not important, just making sure I understand the API
461 			return data;
462 		}
463 
464 		final String name;
465 		final String lot;
466 
467 		ArrayList<Well> wells;
468 		LinkedHashMap<String, Well> tagToCorrectWellMap=new LinkedHashMap<String, Well>();
469 
470 	}
471 
472 	class Well{
473 
474 //		"library_name":"CSNGW",
475 //        "asm_comments":"",
476 //        "instrument_type":"HiSeq-2500 1TB",
477 //        "asm_qc_status":"Pass",
478 //        "dt_created":"2018-05-08 18:05:36",
479 //        "alq_container_barcode":"27-353939",
480 //        "seq_unit_name":"12396.3.255039.ATGCCTG-ACAGGCA.fastq.gz",
481 //        "seq_proj_id":"1190410",
482 //        "raw_reads":8039600,
483 //        "seq_proj_name":"Ensifer meliloti 417 Resequencing",
484 //        "account_jgi_sci_prog":"Microbial",
485 //        "alq_initial_mass_ng":"n.a.",
486 //        "library_protocol":"Regular (DNA)",
487 //        "run_configuration":"2x101",
488 
Well(String name_, JsonObject jo, String plate)489 		public Well(String name_, JsonObject jo, String plate){
490 			name=name_;
491 
492 			library=jo.getString("library_name");
493 			instrument=jo.getString("instrument_type");
494 			date=jo.getString("dt_created");
495 			alq_container_barcode=jo.getString("alq_container_barcode");
496 			seq_unit_name=jo.getString("seq_unit_name");
497 			seq_proj_id=jo.getString("seq_proj_id");
498 			seq_proj_name=jo.getString("seq_proj_name");
499 			Long temp=jo.getLong("raw_reads");
500 			reads=(temp==null ? 0 : temp.longValue());
501 			run_configuration=jo.getString("run_configuration");
502 
503 			if(name.equalsIgnoreCase("X")){return;}
504 			JsonObject kapa=jo.getJson("kapa");
505 			if(kapa==null && outstream!=null){
506 				outstream.println("No Kapa for "+library+", plate "+plate);
507 			}else{
508 				loadKapa(kapa);
509 			}
510 		}
511 
512 //		"hit":77972,
513 //        "name":"tag059",
514 //        "offppm":3.9802975272401615,
515 //        "offhit":32,
516 //        "converted_offtarget_reads_ppm":246.31785207079872,
517 //        "kapa_stats_file":"dna/00/50/16/18//29495471-kapa.stats",
518 //        "pct":0.9698492462311559
519 
loadKapa(JsonObject kapa)520 		void loadKapa(JsonObject kapa){
521 			correctKapaTag=kapa.getString("name");
522 			correctKapaReads=kapa.getLong("hit");
523 			incorrectKapaReads=kapa.getLong("offhit");
524 			Number n=kapa.getNumber("converted_offtarget_reads_ppm");
525 			Class<?> c=n.getClass();
526 			if(c==Double.class){
527 				converted_offtarget_reads_ppm=(Double)n;
528 			}else{
529 				converted_offtarget_reads_ppm=(Long)n;
530 			}
531 			Object[] offwells=kapa.getArray("offwells");
532 
533 			kapaMap=new LinkedHashMap<String, KapaEntry>(3+offwells.length*2);
534 			kapaMap.put(correctKapaTag, new KapaEntry(name, correctKapaReads, correctKapaTag));
535 //			tagToReads.put(correctKapaTag, correctKapaReads);
536 			for(Object o : offwells){
537 				KapaEntry ke=new KapaEntry((Object[])o);
538 //				tagToReads.put(ke.tagName, ke.reads);
539 				kapaMap.put(ke.tagName, ke);
540 			}
541 		}
542 
543 		@Override
toString()544 		public String toString(){
545 			StringBuilder sb=new StringBuilder();
546 			sb.append("name\t"+name).append('\n');
547 			sb.append("correctKapaTag\t"+correctKapaTag).append('\n');
548 			sb.append("reads\t"+reads).append('\n');
549 			sb.append("correctKapaReads\t"+correctKapaReads).append('\n');
550 			sb.append("incorrectKapaReads\t"+incorrectKapaReads).append('\n');
551 			for(KapaEntry e : kapaMap.values()){
552 				sb.append(e.toString()).append('\n');
553 			}
554 			return sb.toString();
555 		}
556 
557 		final String name;
558 
559 		String library;
560 		String instrument;
561 		String date;
562 		String alq_container_barcode;
563 		String seq_unit_name;
564 		String seq_proj_id;
565 		long reads;
566 		String seq_proj_name;
567 		String run_configuration;
568 
569 		String correctKapaTag;
570 		long correctKapaReads;
571 		long incorrectKapaReads;
572 		double converted_offtarget_reads_ppm;
573 
574 		LinkedHashMap<String, KapaEntry> kapaMap;
575 //		LinkedHashMap<String, Long> tagToReads=new LinkedHashMap<String, Long>();
576 
577 //        "asm_comments":"",
578 //        "instrument_type":"HiSeq-2500 1TB",
579 //        "asm_qc_status":"Pass",
580 //        "dt_created":"2018-05-08 18:05:36",
581 //        "alq_container_barcode":"27-353939",
582 //        "seq_unit_name":"12396.3.255039.ATGCCTG-ACAGGCA.fastq.gz",
583 //        "seq_proj_id":"1190410",
584 //        "raw_reads":8039600,
585 //        "seq_proj_name":"Ensifer meliloti 417 Resequencing",
586 //        "account_jgi_sci_prog":"Microbial",
587 //        "alq_initial_mass_ng":"n.a.",
588 //        "library_protocol":"Regular (DNA)",
589 //        "run_configuration":"2x101",
590 
591 	}
592 
593 	class KapaEntry{
594 
595 //		"offwells":[
596 //                    [
597 //                        "A1",
598 //                        0.00017413801681675706,
599 //                        14,
600 //                        "tag001"
601 //                    ],
602 
KapaEntry(Object[] array)603 		KapaEntry(Object[] array){
604 			assert(array.length==4) : Arrays.toString(array);
605 			wellName=(String)array[0];
606 			reads=(Long)array[2];
607 			tagName=(String)array[3];
608 		}
609 
KapaEntry(String wellName_, long reads_, String tagName_)610 		KapaEntry(String wellName_, long reads_, String tagName_){
611 			wellName=wellName_;
612 			reads=reads_;
613 			tagName=tagName_;
614 		}
615 
616 		@Override
toString()617 		public String toString(){
618 			return wellName+"\t"+tagName+"\t"+reads;
619 		}
620 
621 		String wellName;
622 		long reads;
623 		String tagName;
624 
625 	}
626 
627 	//Ugly because it was retrofitted to support unsorted arrays and plate names
628 	class TagData implements Comparable<TagData> {
629 
TagData(String name_, String wellName_)630 		TagData(String name_, String wellName_){
631 			name=name_;
632 			wellName=wellName_;
633 		}
634 
add(String tag, double ppmk, String plate)635 		void add(String tag, double ppmk, String plate){
636 			ArrayList<Double> list=ppmMap.get(tag);
637 			if(list==null){
638 				list=new ArrayList<Double>();
639 				ppmMap.put(tag, list);
640 			}
641 			list.add(ppmk);
642 
643 			ArrayList<String> list2=plateNameMap.get(tag);
644 			if(list2==null){
645 				list2=new ArrayList<String>();
646 				plateNameMap.put(tag, list2);
647 			}
648 			list2.add(plate);
649 		}
650 
getPpmArray(String key, boolean sort)651 		double[] getPpmArray(String key, boolean sort){
652 			ArrayList<Double> list=ppmMap.get(key);
653 			return toPpmArray(list, sort);
654 		}
655 
getPlateNameArray(String key, boolean sort)656 		String[] getPlateNameArray(String key, boolean sort){
657 			ArrayList<String> list=plateNameMap.get(key);
658 			return toPlateArray(list, sort);
659 		}
660 
toPpmArray(ArrayList<Double> list, boolean sort)661 		double[] toPpmArray(ArrayList<Double> list, boolean sort){
662 			if(list==null){return null;}
663 //			double[] array=new double[list.size()];
664 			double[] array=new double[timesSeen];
665 			for(int i=0; i<list.size(); i++){
666 				array[i]=list.get(i);
667 			}
668 			if(sort){Arrays.sort(array);}
669 			return array;
670 		}
671 
toPlateArray(ArrayList<String> list, boolean sort)672 		String[] toPlateArray(ArrayList<String> list, boolean sort){
673 			if(list==null){return null;}
674 //			double[] array=new double[list.size()];
675 			String[] array=new String[timesSeen];
676 			for(int i=0; i<list.size(); i++){
677 				array[i]=list.get(i);
678 			}
679 			if(sort){Arrays.sort(array);}
680 			return array;
681 		}
682 
683 		@Override
compareTo(TagData other)684 		public int compareTo(TagData other) {
685 			return name.compareTo(other.name);
686 		}
687 
688 		final String name;
689 		final String wellName;
690 		int timesSeen=0;
691 
692 		LinkedHashMap<String, ArrayList<Double>> ppmMap=new LinkedHashMap<String, ArrayList<Double>>(203);
693 		LinkedHashMap<String, ArrayList<String>> plateNameMap=new LinkedHashMap<String, ArrayList<String>>(203);
694 
695 	}
696 
697 	/*--------------------------------------------------------------*/
698 	/*----------------            Fields            ----------------*/
699 	/*--------------------------------------------------------------*/
700 
701 	private String in1=null;
702 	private String out1=null;
703 
704 	private String addressPrefix="https://rqc.jgi-psf.org/api/plate_ui/page/";
705 	private String addressSuffix="/kapa spikein";//"/kapa spikein";
706 
707 	private boolean printRaw=false;
708 
709 	/*--------------------------------------------------------------*/
710 
711 	private long linesProcessed=0;
712 	private long linesOut=0;
713 	private long bytesProcessed=0;
714 	private long bytesOut=0;
715 
716 	private long maxLines=Long.MAX_VALUE;
717 
718 	/*--------------------------------------------------------------*/
719 	/*----------------         Final Fields         ----------------*/
720 	/*--------------------------------------------------------------*/
721 
722 	private final FileFormat ffin1;
723 	private final FileFormat ffout1;
724 
725 	private final JsonParser jp=new JsonParser();
726 
727 	private final LinkedHashMap<String, TagData> tagMap=new LinkedHashMap<String, TagData>(203);
728 	private final LinkedHashMap<String, Plate> plateMap=new LinkedHashMap<String, Plate>(203);
729 
730 	final Well dummy=new Well("X", new JsonObject(), "X");
731 
732 	/*--------------------------------------------------------------*/
733 	/*----------------        Common Fields         ----------------*/
734 	/*--------------------------------------------------------------*/
735 
736 	private PrintStream outstream=System.err;
737 	public static boolean verbose=false;
738 	public boolean errorState=false;
739 	private boolean overwrite=false;
740 	private boolean append=false;
741 
742 }
743