1 package shared;
2 
3 import java.io.File;
4 import java.io.PrintStream;
5 import java.lang.management.ManagementFactory;
6 import java.lang.reflect.Method;
7 import java.net.UnknownHostException;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.Collections;
11 import java.util.Comparator;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Random;
15 import java.util.concurrent.ThreadLocalRandom;
16 
17 public class Shared {
18 
19 
20 	/*--------------------------------------------------------------*/
21 	/*----------------         Environment          ----------------*/
22 	/*--------------------------------------------------------------*/
23 
24 
25 	public static boolean ENV=(System.getenv()!=null);
26 	public static boolean WINDOWS=envContainsPair("OS", "Win", true);
27 	public static boolean MAC=envContainsPair("OS", "Mac", true);
28 	//https://stackoverflow.com/questions/14288185/detecting-windows-or-linux
29 	public static boolean LINUX=envContainsPair("OS", "nix", true) || envContainsPair("OS", "nux", true) || envContainsPair("OS", "aix", true);
30 	public static boolean SOLARIS=envContainsPair("OS", "sunos", true);
31 	public static boolean GENEPOOL=envContainsPair("NERSC_HOST", "genepool", false);//Probably deprecated
32 	public static boolean DENOVO=envContainsPair("NERSC_HOST", "denovo", false);//Almost certainly deprecated
33 	public static boolean CORI=envContainsPair("NERSC_HOST", "cori", false);
34 	public static boolean NERSC=envContainsKey("NERSC_HOST");
35 	public static boolean AWS=envContainsKey("EC2_HOME");
36 	public static boolean AMD64="amd64".equalsIgnoreCase(System.getProperty("os.arch"));
37 	private static String HOSTNAME;
38 
setTaxServer(String path)39 	public static void setTaxServer(String path){
40 		taxServerNersc=taxServerAws=path;
41 	}
42 
43 	private static String taxServerNersc="https://taxonomy.jgi.doe.gov/";
44 	private static String ntSketchServerNersc="https://nt-sketch.jgi.doe.gov/";
45 	private static String riboSketchServerNersc="https://ribo-sketch.jgi.doe.gov/";
46 	private static String proteinSketchServerNersc="https://protein-sketch.jgi.doe.gov/";
47 	private static String refseqSketchServerNersc="https://refseq-sketch.jgi.doe.gov/";
48 
49 	private static String taxServerAws="http://bbtaxonomy.org:3068/";
50 	private static String ntSketchServerAws="http://nt-sketch.org:3071/";
51 	private static String riboSketchServerAws="http://ribo-sketch.org:3073/";
52 	private static String proteinSketchServerAws="http://protein-sketch.org:3074/";
53 	private static String refseqSketchServerAws="http://refseq-sketch.org:3072/";
54 
taxServer()55 	public static String taxServer(){return awsServers ? taxServerAws : taxServerNersc;}
ntSketchServer()56 	public static String ntSketchServer(){return awsServers ? ntSketchServerAws : ntSketchServerNersc;}
riboSketchServer()57 	public static String riboSketchServer(){return awsServers ? riboSketchServerAws : riboSketchServerNersc;}
proteinSketchServer()58 	public static String proteinSketchServer(){return awsServers ? proteinSketchServerAws : proteinSketchServerNersc;}
refseqSketchServer()59 	public static String refseqSketchServer(){return awsServers ? refseqSketchServerAws : refseqSketchServerNersc;}
60 
61 	public static boolean awsServers=false;
62 
envContainsPair(String key, String value, boolean loose)63 	public static boolean envContainsPair(String key, String value, boolean loose){
64 		Map<String, String> map=System.getenv();
65 		String v=map.get(key);
66 		if(value==null || v==null){return v==value;}
67 		return loose ? v.contains(value.toLowerCase()) : value.equalsIgnoreCase(v);
68 	}
69 
envContainsKey(String key)70 	public static boolean envContainsKey(String key){
71 		Map<String, String> map=System.getenv();
72 		return map.containsKey(key);
73 	}
74 
HOSTNAME()75 	public static String HOSTNAME(){
76 		if(HOSTNAME==null){
77 			try {
78 				java.net.InetAddress localMachine = java.net.InetAddress.getLocalHost();
79 				HOSTNAME=localMachine.getHostName();
80 			} catch (UnknownHostException e) {
81 				// TODO Auto-generated catch block
82 //				e.printStackTrace();
83 				HOSTNAME="unknown";
84 			} catch (NullPointerException e) {
85 				// TODO Auto-generated catch block
86 //				e.printStackTrace();
87 				HOSTNAME="unknown";
88 			} catch (Throwable e) {
89 				HOSTNAME="unknown";
90 			}
91 		}
92 		return HOSTNAME;
93 	}
94 
95 
96 	/*--------------------------------------------------------------*/
97 	/*----------------            Stuff             ----------------*/
98 	/*--------------------------------------------------------------*/
99 
main(String[] args)100 	public static void main(String[] args){
101 		COMMAND_LINE=args;
102 		mainClass=Shared.class;
103 		assert(false) : fullCommandline();
104 	}
105 
106 	public static int LOGICAL_PROCESSORS=CALC_LOGICAL_PROCESSORS();
107 	private static int THREADS=setThreads(-1);
108 
109 	private static int READ_BUFFER_NUM_BUFFERS=setBuffers();
110 	private static int READ_BUFFER_LENGTH=200;
111 	private static long READ_BUFFER_MAX_DATA=400000;
112 
113 	public static boolean OUTPUT_KMG=true;
114 
115 	/** Temporary, for testing; should be made non-global */
116 	public static boolean AMINO_IN=false;
117 
118 	//TODO:  For some reason, it seems as though GAPBUFFER must equal exactly 1/2 of GAPLEN.  Not good; 1/4 would be far better.
119 
120 	public static final int GAPBUFFER=64; //TODO:  Seems to break less than 64, for some reason
121 	public static final int GAPBUFFER2=2*GAPBUFFER;
122 	public static final int GAPLEN=128; //TODO: May break when over 128
123 	public static final int MINGAP=GAPBUFFER2+GAPLEN;
124 	public static final int GAPCOST=Tools.max(1, GAPLEN/64);
125 	public static final byte GAPC='-';
126 
127 	public static String BBMAP_VERSION_STRING="38.92";
128 	public static String BBMAP_VERSION_NAME="Stable Shuffle";
129 
130 	public static boolean TRIM_READ_COMMENTS=false;
131 	public static boolean TRIM_RNAME=false; //For mapped sam reads
132 
133 	public static boolean USE_JNI=(CORI || DENOVO || GENEPOOL || NERSC || AWS || (AMD64 && (LINUX || MAC))) && !WINDOWS;
134 	public static boolean USE_MPI=false;
135 	public static boolean MPI_KEEP_ALL=true;
136 	/** Use ConcurrentReadInputStreamMPI instead of D for this instance */
137 	public static boolean USE_CRISMPI=true;
138 	public static int MPI_RANK=0;
139 	public static int MPI_NUM_RANKS=1;
140 
141 	public static int FASTA_WRAP=70;
142 	public static byte FAKE_QUAL=30;
143 
144 	public static boolean FIX_EXTENSIONS=true;
145 
146 	/** True if assertions are enabled. */
147 	private static boolean EA=false;
148 
EA()149 	public static boolean EA(){return EA;}
150 
151 	public static String BBMAP_CLASS=null;
152 	public static Class<?> mainClass=null;
153 	public static String[] COMMAND_LINE=null;
154 
155 	public static final byte PLUS=0;
156 	public static final byte MINUS=1;
157 	/** Index with strand number */
158 	public static final String[] strandCodes={"+", "-", "?"};
159 	public static final char[] strandCodes2={'+', '-', '?'};
160 
JVM_ARGS()161 	public static List<String> JVM_ARGS(){
162 		return ManagementFactory.getRuntimeMXBean().getInputArguments();
163 	}
164 
fullCommandline()165 	public static String fullCommandline(){
166 		StringBuilder sb=new StringBuilder();
167 		sb.append("java ");
168 		for(String s : JVM_ARGS()) {
169 			sb.append(s).append(' ');
170 		}
171 		sb.append("-cp "+System.getProperty("java.class.path")+" ");
172 		sb.append(mainClass.getCanonicalName()).append(' ');
173 		for(String s : COMMAND_LINE) {
174 			sb.append(s).append(' ');
175 		}
176 		sb.setLength(sb.length()-1);
177 		return sb.toString();
178 	}
179 
180 	/** Directory in which to write temp files */
181 	private static String TMPDIR=getTmpdir();
182 //	static{assert(false) : "TMPDIR="+TMPDIR;}
183 
getTmpdir()184 	private static String getTmpdir(){
185 		String s=System.getenv("SLURM_TMP");
186 		if(s==null){s=System.getenv("TMPDIR");}
187 		if(s!=null){s=(s+"/").replaceAll("//", "/").replaceAll("\\\\", "/");}
188 		return s;
189 	}
190 
tmpdir()191 	public static String tmpdir(){return TMPDIR;}
192 
setTmpdir(String s)193 	public static String setTmpdir(String s){
194 		if(s==null){TMPDIR=null;}
195 		else{
196 			s=s.replaceAll("\\\\", "/");
197 			if(!s.endsWith("/")){s=s+"/";}
198 			TMPDIR=s.replaceAll("//", "/");
199 		}
200 		return TMPDIR;
201 	}
202 
203 	/** Anomaly probably resolved as of v.20.1
204 	 * This variable should be TRUE for normal users and FALSE for me. */
205 	public static boolean anomaly=!(System.getProperty("user.dir")+"").contains("/bushnell/") && !WINDOWS;
206 
getTLCB(int len)207 	public static final char[] getTLCB(int len){
208 		char[] buffer=TLCB.get();
209 		if(buffer==null || buffer.length<len){
210 			buffer=new char[len];
211 			if(len<1000000){TLCB.set(buffer);}
212 		}
213 		return buffer;
214 	}
215 	private static final ThreadLocal<char[]> TLCB=new ThreadLocal<char[]>();
216 
217 	/*--------------------------------------------------------------*/
218 	/*----------------           Threads            ----------------*/
219 	/*--------------------------------------------------------------*/
220 
capThreads(int t)221 	public static int capThreads(int t) {
222 		assert(THREADS>0) : THREADS;
223 		final int old=THREADS;
224 		THREADS=Tools.mid(1, t, old);
225 		assert(THREADS>0) : THREADS;
226 		return old;
227 	}
228 
setThreads(String x)229 	public static int setThreads(String x){
230 		int y=LOGICAL_PROCESSORS;
231 		if(x!=null && !x.equalsIgnoreCase("auto")){
232 			if(x.indexOf('.')>=0){
233 				double d=Double.parseDouble(x);
234 				if(d>1){
235 					y=(int)d;
236 				}else{
237 					y=(int)Tools.max(1, Math.ceil(d*y));
238 				}
239 			}else{
240 				y=Integer.parseInt(x);
241 			}
242 		}
243 		return setThreads(y);
244 	}
245 
setThreads(int x)246 	public static int setThreads(int x){
247 		if(x>0){
248 			THREADS=x;
249 		}else{
250 			THREADS=Tools.max(1, LOGICAL_PROCESSORS);
251 		}
252 		setBuffers();
253 		assert(THREADS>0) : THREADS;
254 		return THREADS;
255 	}
256 
threads()257 	public static int threads(){
258 		assert(THREADS>0) : THREADS;
259 		return THREADS;
260 	}
261 
CALC_LOGICAL_PROCESSORS()262 	public static int CALC_LOGICAL_PROCESSORS(){
263 		final int procs=Tools.max(1, Runtime.getRuntime().availableProcessors());
264 		int slots=procs;
265 		Map<String,String> env=System.getenv();
266 		String s=env.get("NSLOTS");//Genepool
267 		boolean success=false;
268 		if(s!=null){
269 			int x=slots;
270 			try {
271 				x=Tools.max(1, Integer.parseInt(s));
272 				success=true;
273 			} catch (NumberFormatException e) {
274 				//ignore
275 			}
276 			if(x<=16){slots=x;}
277 		}
278 		if(!success){
279 			s=env.get("SLURM_CPUS_ON_NODE");//All SLURM systems
280 			if(s!=null){
281 				int x=slots;
282 				try {
283 					x=Tools.max(1, Integer.parseInt(s));
284 					success=true;
285 				} catch (NumberFormatException e) {
286 					//ignore
287 				}
288 				slots=x;
289 			}
290 		}
291 
292 //		if(slots>8 && (slots*2==procs || (slots==16 && procs==40))){return procs;}//hyperthreading
293 		return Tools.min(slots, procs);
294 	}
295 
296 	/*--------------------------------------------------------------*/
297 	/*----------------             JNI              ----------------*/
298 	/*--------------------------------------------------------------*/
299 
300 	private static int loadedJNI=-1;
loadJNI()301 	public static synchronized boolean loadJNI(){
302 		return loadJNI("bbtoolsjni");
303 	}
loadJNI(String name)304 	public static synchronized boolean loadJNI(String name){
305 		if(loadedJNI<0){
306 			boolean success=false;
307 
308 			String libpath=System.getProperty("java.library.path");
309 //			System.err.println("libpath='"+libpath+"'");
310 
311 			if(libpath==null || libpath.length()==0){
312 				libpath=System.getProperty("java.class.path").replace("/current", "/jni");
313 			}else if(!libpath.contains("/jni")){
314 				libpath=libpath+":"+System.getProperty("java.class.path").replace("/current", "/jni");
315 			}
316 
317 			//Normal load
318 			try{
319 				System.loadLibrary(name);
320 				success=true;
321 			}catch(UnsatisfiedLinkError e){}
322 
323 			if(!success){
324 				// System.loadLibrary() does not work with MPI.
325 				// Need to use System.load() with an explicit full
326 				// path to the native library file for the MPI case.
327 //				String libpath=System.getProperty("java.library.path");
328 				libpath=libpath.replace("-Djava.library.path=","");
329 				String[] libpathEntries=libpath.split(File.pathSeparator);
330 				for(int i=0; i<libpathEntries.length && !success; i++){
331 					String lib=libpathEntries[i]+"/"+System.mapLibraryName(name);
332 					try{
333 						System.load(lib);
334 						success=true;
335 					}catch(UnsatisfiedLinkError e2){
336 						success=false;
337 					}
338 				}
339 			}
340 
341 			if(success){
342 				loadedJNI=1;
343 			}else{
344 				loadedJNI=0;
345 				System.err.println("Native library can not be found in java.library.path.");
346 				new Exception().printStackTrace();
347 				System.exit(1);
348 			}
349 		}
350 		return loadedJNI==1;
351 	}
352 
353 	/*--------------------------------------------------------------*/
354 	/*----------------           Buffers            ----------------*/
355 	/*--------------------------------------------------------------*/
356 
capBuffers(int num)357 	public static int capBuffers(int num){
358 		return setBuffers(Tools.min(num, READ_BUFFER_NUM_BUFFERS));
359 	}
360 
READ_BUFFER_NUM_BUFFERS()361 	public static int READ_BUFFER_NUM_BUFFERS() {
362 		return READ_BUFFER_NUM_BUFFERS;
363 	}
364 
setBuffers()365 	public static int setBuffers(){
366 		return setBuffersFromThreads(THREADS);
367 	}
368 
setBuffersFromThreads(int threads)369 	public static int setBuffersFromThreads(int threads){
370 		return setBuffers(Tools.max(4, (threads*3)/2));
371 	}
372 
373 	/** Number of read lists buffered in input streams */
setBuffers(int num)374 	public static int setBuffers(int num){
375 //		assert(READ_BUFFER_NUM_BUFFERS==0 || READ_BUFFER_NUM_BUFFERS==num) : READ_BUFFER_NUM_BUFFERS+" -> "+num; //TODO: 123
376 		num=Tools.max(2, num);//TODO: Interesting, this is min 2 instead of the expected 1; maybe 1 didn't work?
377 		return READ_BUFFER_NUM_BUFFERS=num;
378 	}
379 
380 	/** Number of read lists buffered in input streams */
numBuffers()381 	public static int numBuffers(){
382 		return READ_BUFFER_NUM_BUFFERS;
383 	}
384 
bufferLen()385 	public static int bufferLen(){
386 		return READ_BUFFER_LENGTH;
387 	}
388 
bufferData()389 	public static long bufferData(){
390 		return READ_BUFFER_MAX_DATA;
391 	}
392 
capBufferLen(int x)393 	public static void capBufferLen(int x){
394 		if(x!=READ_BUFFER_LENGTH){setBufferLen(Tools.min(x, READ_BUFFER_LENGTH));}
395 	}
396 
setBufferLen(int x)397 	public static int setBufferLen(int x){
398 //		assert(false) : READ_BUFFER_LENGTH+" -> "+x; //TODO: 123
399 		assert(x>0);
400 		return READ_BUFFER_LENGTH=x;
401 	}
402 
setBufferData(long x)403 	public static long setBufferData(long x){
404 //		assert(false) : READ_BUFFER_MAX_DATA+" -> "+x; //TODO: 123
405 		assert(x>0);
406 		return READ_BUFFER_MAX_DATA=x;
407 	}
408 
409 
410 	/*--------------------------------------------------------------*/
411 	/*----------------            Memory            ----------------*/
412 	/*--------------------------------------------------------------*/
413 
414 	public static boolean LOW_MEMORY=false;
415 
416 	/** Ratio of -Xms to -Xmx parameters */
xmsRatio()417 	public static final double xmsRatio(){
418 		Runtime rt=Runtime.getRuntime();
419 		return rt.totalMemory()*1.0/rt.maxMemory();
420 	}
421 
memAvailable(int readThreads)422 	public static long memAvailable(int readThreads){
423 		long usableMemory;
424 		{
425 			long memory=Runtime.getRuntime().maxMemory();
426 			double xmsRatio=Shared.xmsRatio();
427 			usableMemory=(long)Tools.max(((memory-48000000-(Tools.max(readThreads, 4)*400000))*(xmsRatio>0.97 ? 0.82 : 0.72)), memory*0.45);
428 		}
429 		return usableMemory;
430 	}
431 
memTotal()432 	public static long memTotal(){
433 		Runtime rt=Runtime.getRuntime();
434 		return rt.maxMemory();
435 	}
436 
memFree()437 	public static long memFree(){
438 		Runtime rt=Runtime.getRuntime();
439 		return rt.freeMemory();
440 	}
441 
memAvailable()442 	public static long memAvailable(){
443 		Runtime rt=Runtime.getRuntime();
444 		return rt.maxMemory()-rt.totalMemory()+rt.freeMemory();
445 	}
446 
447 	/** An estimate, for preallocation */
memAvailableAdvanced()448 	public static long memAvailableAdvanced(){
449 		Runtime rt=Runtime.getRuntime();
450 		final long mmemory=rt.maxMemory();
451 		final long tmemory=rt.totalMemory();
452 		final long fmemory=rt.freeMemory();
453 		final long umemory=tmemory-fmemory;
454 
455 		double xmsRatio=Shared.xmsRatio();
456 		double usableMemory=Tools.max(((mmemory-96000000)*(xmsRatio>0.97 ? 0.82 : 0.72)), mmemory*0.45);
457 		double availableMemory=Tools.max(fmemory*0.5, usableMemory-umemory); //The 0.5 is an untested conservative guess
458 
459 
460 		return (long)availableMemory;
461 	}
462 
memUsed()463 	public static long memUsed(){
464 		Runtime rt=Runtime.getRuntime();
465 		return rt.maxMemory()-rt.freeMemory();
466 	}
467 
468 	/** Print statistics about current memory use and availability */
printMemory()469 	public static final void printMemory(){
470 		try{
471 			if(GC_BEFORE_PRINT_MEMORY){
472 				System.gc();
473 				System.gc();
474 			}
475 			Runtime rt=Runtime.getRuntime();
476 			long mmemory=rt.maxMemory()/1000000;
477 			long tmemory=rt.totalMemory()/1000000;
478 			long fmemory=rt.freeMemory()/1000000;
479 			long umemory=tmemory-fmemory;
480 			System.err.println("Memory: "+"max="+mmemory+"m, total="+tmemory+"m, "+"free="+fmemory+"m, used="+umemory+"m");
481 		}catch(Throwable t){}
482 	}
483 
threadLocalRandom()484 	public static final Random threadLocalRandom(){return threadLocalRandom(-1);}
threadLocalRandom(long seed)485 	public static final Random threadLocalRandom(long seed){
486 		Random randy;
487 		if(seed>=0){return new Random(seed);}//ThreadLocalRandom does not support seeds
488 		try {
489 			randy=ThreadLocalRandom.current();
490 		} catch (Throwable e) {//In case the JVM does not support ThreadLocalRandom;
491 			randy=new Random();
492 		}
493 		return randy;
494 	}
495 
496 	/** Do garbage collection prior to printing memory usage */
497 	public static boolean GC_BEFORE_PRINT_MEMORY=false;
498 
499 	/** For Matt N. */
500 	public static String comment;
501 
502 	/*--------------------------------------------------------------*/
503 	/*----------------            Java 8            ----------------*/
504 	/*--------------------------------------------------------------*/
505 
sort(int[] array)506 	public static final void sort(int[] array){sort(array, 0, array.length);}
sort(int[] array, int from, int to)507 	public static final void sort(int[] array, int from, int to){
508 		try {
509 			if(!parallelSort || array.length<=parallelSortLength){
510 				Arrays.sort(array, from, to); //Supported in pre-Java 8.
511 				return;
512 			}
513 			Arrays.parallelSort(array, from, to);
514 		} catch (OutOfMemoryError e) {
515 			KillSwitch.memKill(e);
516 		}
517 	}
518 
sort(long[] array)519 	public static final void sort(long[] array){sort(array, 0, array.length);}
sort(long[] array, int from, int to)520 	public static final void sort(long[] array, int from, int to){
521 		try {
522 			if(!parallelSort || array.length<=parallelSortLength || THREADS<2){
523 				Arrays.sort(array, from, to); //Supported in pre-Java 8.
524 				return;
525 			}
526 			Arrays.parallelSort(array, from, to);
527 		} catch (OutOfMemoryError e) {
528 			KillSwitch.memKill(e);
529 		}
530 	}
531 
sort(float[] array)532 	public static final void sort(float[] array){sort(array, 0, array.length);}
sort(float[] array, int from, int to)533 	public static final void sort(float[] array, int from, int to){
534 		try {
535 			if(!parallelSort || array.length<=parallelSortLength){
536 				Arrays.sort(array, from, to); //Supported in pre-Java 8.
537 				return;
538 			}
539 			Arrays.parallelSort(array, from, to);
540 		} catch (OutOfMemoryError e) {
541 			KillSwitch.memKill(e);
542 		}
543 	}
544 
sort(double[] array)545 	public static final void sort(double[] array){sort(array, 0, array.length);}
sort(double[] array, int from, int to)546 	public static final void sort(double[] array, int from, int to){
547 		try {
548 			if(!parallelSort || array.length<=parallelSortLength){
549 				Arrays.sort(array, from, to); //Supported in pre-Java 8.
550 				return;
551 			}
552 			Arrays.parallelSort(array, from, to);
553 		} catch (OutOfMemoryError e) {
554 			KillSwitch.memKill(e);
555 		}
556 	}
557 
sort(T[] array)558 	public static final <T extends Comparable<? super T>> void sort(T[] array){sort(array, 0, array.length);}
sort(T[] array, int from, int to)559 	public static final <T extends Comparable<? super T>> void sort(T[] array, int from, int to){
560 		try {
561 			if(!parallelSort || array.length<=parallelSortLength || THREADS<2){
562 				Arrays.sort(array, from, to); //Supported in pre-Java 8.
563 				return;
564 			}
565 			Arrays.parallelSort(array, from, to);
566 		} catch (OutOfMemoryError e) {
567 			KillSwitch.memKill(e);
568 		}
569 	}
570 
sort(ArrayList<T> list)571 	public static final <T extends Comparable<? super T>> void sort(ArrayList<T> list){
572 		try {
573 			if(!parallelSort || list.size()<=parallelSortLength || THREADS<2){
574 				Collections.sort(list); //Supported in pre-Java 8.
575 				return;
576 			}
577 
578 			{//If this block causes compile errors, just replace the whole function body with "Shared.sort(list, comparator);"
579 				@SuppressWarnings("unchecked")
580 				T[] array=list.toArray((T[])new Comparable[0]);
581 				list.clear();
582 				Arrays.parallelSort(array);
583 				for(T r : array){list.add(r);}
584 			}
585 		} catch (OutOfMemoryError e) {
586 			KillSwitch.memKill(e);
587 		}
588 	}
589 
sort(ArrayList<T> list, Comparator<? super T> comparator)590 	public static final <T> void sort(ArrayList<T> list, Comparator<? super T> comparator){
591 		try {
592 			if(!parallelSort){
593 				Collections.sort(list, comparator); //Supported in pre-Java 8.
594 				return;
595 			}
596 
597 			{//If this block causes compile errors, just replace the whole function body with "Shared.sort(list, comparator);"
598 				if(list.size()<=parallelSortLength || THREADS<2){
599 					list.sort(comparator);
600 					return;
601 				}
602 				@SuppressWarnings("unchecked")
603 				T[] array=list.toArray((T[])new Object[0]);
604 				list.clear();
605 				Arrays.parallelSort(array, comparator);
606 				for(T r : array){list.add(r);}
607 			}
608 		} catch (OutOfMemoryError e) {
609 			KillSwitch.memKill(e);
610 		}
611 	}
612 
613 	/**
614 	 * Close this output stream if it is not stderr or stdout.
615 	 * Intended for files.
616 	 * @param outstream
617 	 */
closeStream(PrintStream outstream)618 	public static void closeStream(PrintStream outstream) {
619 		if(outstream!=null){
620 			synchronized(outstream){
621 				if(outstream!=System.err && outstream!=System.out){
622 					outstream.close();
623 				}
624 			}
625 		}
626 	}
627 
628 	public static final long MAX_ARRAY_LEN=Integer.MAX_VALUE-20;
629 
630 	public static final int parallelSortLength=10000;
631 	public static boolean disableParallelSort=false;
632 	public static boolean parallelSort=testParallelSort();
633 
634 	public static double javaVersion=parseJavaVersion();
635 
parseJavaVersion()636 	private static double parseJavaVersion(){
637 		String s=System.getProperty("java.version");
638 		if(s==null){return 1.6;}
639 		int dots=0;
640 		StringBuilder sb=new StringBuilder();
641 		for(int i=0; i<s.length() && dots<2; i++){
642 			char c=s.charAt(i);
643 			if(c=='.'){dots++;}
644 			else if(!Tools.isDigit(c)){break;}
645 			if(dots>1){break;}
646 			sb.append(c);
647 		}
648 		return Double.parseDouble(sb.toString());
649 	}
650 
setParallelSort(boolean x)651 	public static void setParallelSort(boolean x){
652 		if(x){
653 			disableParallelSort=false;
654 			parallelSort=testParallelSort();
655 		}else{
656 			disableParallelSort=true;
657 			parallelSort=false;
658 		}
659 	}
660 
661 	/** This tests to see if the java version supports parallel sort, which was not true prior to 1.8. */
testParallelSort()662 	private static boolean testParallelSort(){
663 		Method m=null;
664 		try {
665 			m=Arrays.class.getMethod("parallelSort", new Class[] {Object[].class, Comparator.class});
666 		} catch (NoSuchMethodException e) {
667 			// TODO Auto-generated catch block
668 //			e.printStackTrace();
669 		} catch (SecurityException e) {
670 			// TODO Auto-generated catch block
671 //			e.printStackTrace();
672 		} catch (Throwable t) {
673 			// TODO Auto-generated catch block
674 //			e.printStackTrace();
675 		}
676 		return m!=null;
677 	}
678 
679 	static{
assert(EA=true)680 		assert(EA=true);//Sets the EA flag
KillSwitch.addBallast()681 		KillSwitch.addBallast();
682 	}
683 
684 }
685