[med-svn] [beagle] 03/04: Imported Upstream version 4.1~160503-862+dfsg
Dylan Aïssi
bob.dybian-guest at moszumanska.debian.org
Mon May 30 20:42:38 UTC 2016
This is an automated email from the git hooks/post-receive script.
bob.dybian-guest pushed a commit to branch master
in repository beagle.
commit ef336ca3d3f6ce56f78ab7440592f8d62e564a90
Author: Dylan Aïssi <bob.dybian at gmail.com>
Date: Fri May 6 10:51:44 2016 +0200
Imported Upstream version 4.1~160503-862+dfsg
---
beagleutil/BasicIntInterval.java | 296 ++++----
blbutil/Const.java | 186 ++---
blbutil/IndexMap.java | 390 +++++-----
blbutil/IndexSet.java | 304 ++++----
blbutil/InputIt.java | 640 ++++++++--------
blbutil/StringUtil.java | 562 +++++++-------
blbutil/Utilities.java | 416 +++++------
dag/MergeableDagLevel.java | 1460 ++++++++++++++++++-------------------
gpl_license | 1348 +++++++++++++++++-----------------
haplotype/RefHapPairs.java | 378 +++++-----
ibd/HaploidIbd.java | 528 +++++++-------
main/ConstrainedAlleleProbs.java | 37 +-
main/Main.java | 16 +-
main/WindowWriter.java | 24 +-
sample/DuoBaumLevel.java | 1282 ++++++++++++++++----------------
sample/DuoNodes.java | 676 ++++++++---------
sample/HapNodes.java | 466 ++++++------
sample/RecombSingleBaumLevel.java | 8 +-
sample/RecombSingleNodes.java | 722 +++++++++---------
sample/SingleBaumLevel.java | 8 +-
sample/SingleNodes.java | 588 +++++++--------
vcf/AllData.java | 710 +++++++++---------
vcf/GprobsStatistics.java | 106 +--
vcf/Markers.java | 778 ++++++++++----------
vcf/VcfWindow.java | 642 ++++++++--------
vcf/VcfWriter.java | 140 +++-
26 files changed, 6399 insertions(+), 6312 deletions(-)
diff --git a/beagleutil/BasicIntInterval.java b/beagleutil/BasicIntInterval.java
index ed1d786..b7814d4 100644
--- a/beagleutil/BasicIntInterval.java
+++ b/beagleutil/BasicIntInterval.java
@@ -1,148 +1,148 @@
-/*
- * Copyright 2014 Brian L. Browning
- *
- * 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 beagleutil;
-
-/**
- * <p>Class {@code BasicIntInterval} represents an interval of
- * consecutive integers.
- * </p>
- * Instances of class {@code BasicIntInterval} are immutable.
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
-
-} */
-public final class BasicIntInterval implements IntInterval,
- Comparable<BasicIntInterval> {
-
- private final int start;
- private final int end;
-
- /**
- * Constructs an {@code SimpleIntInterval} instance.
- * @param start the starting integer (inclusive).
- * @param end the ending integer (inclusive).
- * @throws IllegalArgumentException if {@code start>end}.
- */
- public BasicIntInterval(int start, int end) {
- if (start > end) {
- String s = "start=" + start + " end=" + end;
- throw new IllegalArgumentException(s);
- }
- this.start = start;
- this.end = end;
- }
-
- @Override
- public int start() {
- return start;
- }
-
- @Override
- public int end() {
- return end;
- }
-
- /**
- * <p>Returns a hash code value for the object.
- * </p>
- * <p>The hash code is defined by the following calculation:
- * </p>
- * <pre>
- int hash = 3;
- hash += 59 * hash + this.start();
- hash += 59 * hash + this.end();
- * </pre>
- * @return a hash code value for the object.
- */
- @Override
- public int hashCode() {
- int hash = 3;
- hash = 59 * hash + this.start;
- hash = 59 * hash + this.end;
- return hash;
- }
-
- /**
- * Returns {@code true} if the specified object is an
- * {@code BasicIntInterval} instance and
- * {@code this.start() == ((BasicIntInterval) obj).start()}, and
- * {@code this.end() == ((BasicIntInterval) obj).end()},
- * and returns {@code false} otherwise.
- *
- * @param obj the object to be compared with {@code this} for equality.
- * @return {@code true} if the specified object is equal to
- * {@code this}, and returns false otherwise.
- */
- @Override
- public boolean equals(Object obj) {
- if (this==obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final BasicIntInterval other = (BasicIntInterval) obj;
- if (this.start != other.start) {
- return false;
- }
- return this.end==other.end;
- }
-
- /**
- * Compares the specified {@code BasicIntInterval} with this for order,
- * and returns a negative integer, zero, or a positive integer as
- * {@code this} is less than, equal to, or greater than the specified
- * {@code BasicIntInterval} object.
- * {@code BasicIntInterval} objects are
- * ordered by their start and their end values in that order.
- *
- * @param o the {@code BasicIntInterval} to be compared with this.
- *
- * @return a negative integer, zero, or a positive integer as this
- * object is less than, equal to, or greater than the specified object.
- */
- @Override
- public int compareTo(BasicIntInterval o) {
- if (this.start != o.start) {
- return this.start < o.start ? -1 : 1;
- }
- else if (this.end != o.end) {
- return this.end < o.end ? -1 : 1;
- }
- return 0;
- }
-
- /**
- * Returns a string representation of {@code this}. The exact
- * details of the representation are unspecified and subject to change.
- *
- * @return a string representation of {@code this}. The exact
- * details of the representation are unspecified and subject to change.
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append('[');
- sb.append(start);
- sb.append(", ");
- sb.append(end);
- sb.append(']');
- return sb.toString();
- }
-}
+/*
+ * Copyright 2014 Brian L. Browning
+ *
+ * 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 beagleutil;
+
+/**
+ * <p>Class {@code BasicIntInterval} represents an interval of
+ * consecutive integers.
+ * </p>
+ * Instances of class {@code BasicIntInterval} are immutable.
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+
+} */
+public final class BasicIntInterval implements IntInterval,
+ Comparable<BasicIntInterval> {
+
+ private final int start;
+ private final int end;
+
+ /**
+ * Constructs an {@code SimpleIntInterval} instance.
+ * @param start the starting integer (inclusive).
+ * @param end the ending integer (inclusive).
+ * @throws IllegalArgumentException if {@code start>end}.
+ */
+ public BasicIntInterval(int start, int end) {
+ if (start > end) {
+ String s = "start=" + start + " end=" + end;
+ throw new IllegalArgumentException(s);
+ }
+ this.start = start;
+ this.end = end;
+ }
+
+ @Override
+ public int start() {
+ return start;
+ }
+
+ @Override
+ public int end() {
+ return end;
+ }
+
+ /**
+ * <p>Returns a hash code value for the object.
+ * </p>
+ * <p>The hash code is defined by the following calculation:
+ * </p>
+ * <pre>
+ int hash = 3;
+ hash += 59 * hash + this.start();
+ hash += 59 * hash + this.end();
+ * </pre>
+ * @return a hash code value for the object.
+ */
+ @Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 59 * hash + this.start;
+ hash = 59 * hash + this.end;
+ return hash;
+ }
+
+ /**
+ * Returns {@code true} if the specified object is an
+ * {@code BasicIntInterval} instance and
+ * {@code this.start() == ((BasicIntInterval) obj).start()}, and
+ * {@code this.end() == ((BasicIntInterval) obj).end()},
+ * and returns {@code false} otherwise.
+ *
+ * @param obj the object to be compared with {@code this} for equality.
+ * @return {@code true} if the specified object is equal to
+ * {@code this}, and returns false otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this==obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final BasicIntInterval other = (BasicIntInterval) obj;
+ if (this.start != other.start) {
+ return false;
+ }
+ return this.end==other.end;
+ }
+
+ /**
+ * Compares the specified {@code BasicIntInterval} with this for order,
+ * and returns a negative integer, zero, or a positive integer as
+ * {@code this} is less than, equal to, or greater than the specified
+ * {@code BasicIntInterval} object.
+ * {@code BasicIntInterval} objects are
+ * ordered by their start and their end values in that order.
+ *
+ * @param o the {@code BasicIntInterval} to be compared with this.
+ *
+ * @return a negative integer, zero, or a positive integer as this
+ * object is less than, equal to, or greater than the specified object.
+ */
+ @Override
+ public int compareTo(BasicIntInterval o) {
+ if (this.start != o.start) {
+ return this.start < o.start ? -1 : 1;
+ }
+ else if (this.end != o.end) {
+ return this.end < o.end ? -1 : 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns a string representation of {@code this}. The exact
+ * details of the representation are unspecified and subject to change.
+ *
+ * @return a string representation of {@code this}. The exact
+ * details of the representation are unspecified and subject to change.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ sb.append(start);
+ sb.append(", ");
+ sb.append(end);
+ sb.append(']');
+ return sb.toString();
+ }
+}
diff --git a/blbutil/Const.java b/blbutil/Const.java
index a7f4ccf..46d8ad2 100644
--- a/blbutil/Const.java
+++ b/blbutil/Const.java
@@ -1,93 +1,93 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package blbutil;
-
-/**
- * Class {@code Const} provides public static final fields with
- * string and character constants.
- *
- * @author Brian L Browning
- */
-public class Const {
-
- private Const() {
- // private constructor to prevent instantiation.
- }
-
- /**
- * The system-dependent string representing a new line-line:
- * {@code System.getProperty("line.separator")}
- */
- public static final String nl = System.getProperty("line.separator");
-
- /**
- * The VCF missing-data symbol as a string: {@code "."}
- */
- public static final String MISSING_DATA_STRING = ".";
-
- /**
- * The VCF missing-data symbol as a character: {@code '.'}
- */
- public static final char MISSING_DATA_CHAR = '.';
-
- /**
- * The colon character: {@code ':'}
- */
- public static final char colon = ':';
-
- /**
- * The hyphen character: {@code '-'}
- */
- public static final char hyphen = '-';
-
- /**
- * The tab character: {@code '\t'}
- */
- public static final char tab = '\t';
-
- /**
- * The semicolon character: {@code ';'}
- */
- public static final char semicolon = ';';
-
- /**
- * The comma character: {@code ','}
- */
- public static final char comma = ',';
-
- /**
- * The phased allele separator: {@code '|'}
- */
- public static final char phasedSep = '|';
-
- /**
- * The unphased allele separator: {@code '/'}
- */
- public static final char unphasedSep = '/';
-
- /**
- * The value 1,000,000,000
- */
- public static final int giga = 1000000000;
-
- /**
- * The value 1,000,000
- */
- public static final int mega = 1000000;
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package blbutil;
+
+/**
+ * Class {@code Const} provides public static final fields with
+ * string and character constants.
+ *
+ * @author Brian L Browning
+ */
+public class Const {
+
+ private Const() {
+ // private constructor to prevent instantiation.
+ }
+
+ /**
+ * The system-dependent string representing a new line-line:
+ * {@code System.getProperty("line.separator")}
+ */
+ public static final String nl = System.getProperty("line.separator");
+
+ /**
+ * The VCF missing-data symbol as a string: {@code "."}
+ */
+ public static final String MISSING_DATA_STRING = ".";
+
+ /**
+ * The VCF missing-data symbol as a character: {@code '.'}
+ */
+ public static final char MISSING_DATA_CHAR = '.';
+
+ /**
+ * The colon character: {@code ':'}
+ */
+ public static final char colon = ':';
+
+ /**
+ * The hyphen character: {@code '-'}
+ */
+ public static final char hyphen = '-';
+
+ /**
+ * The tab character: {@code '\t'}
+ */
+ public static final char tab = '\t';
+
+ /**
+ * The semicolon character: {@code ';'}
+ */
+ public static final char semicolon = ';';
+
+ /**
+ * The comma character: {@code ','}
+ */
+ public static final char comma = ',';
+
+ /**
+ * The phased allele separator: {@code '|'}
+ */
+ public static final char phasedSep = '|';
+
+ /**
+ * The unphased allele separator: {@code '/'}
+ */
+ public static final char unphasedSep = '/';
+
+ /**
+ * The value 1,000,000,000
+ */
+ public static final int giga = 1000000000;
+
+ /**
+ * The value 1,000,000
+ */
+ public static final int mega = 1000000;
+}
diff --git a/blbutil/IndexMap.java b/blbutil/IndexMap.java
index b15ea4a..0ca3fd8 100644
--- a/blbutil/IndexMap.java
+++ b/blbutil/IndexMap.java
@@ -1,195 +1,195 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package blbutil;
-
-import java.util.Arrays;
-
-/**
- * <p>Class {@code IndexMap} is a map whose keys are a bounded set of
- * non-negative integers and whose values are integers.
- * </p>
- * <p>Class {@code IndexMap} supports a {@code clear()} method, but it does not
- * support a {@code remove()} method.
- * </p>
- * <p>Class {@code IndexMap} is not thread-safe.
- * </p>
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class IndexMap {
-
- private final int nil;
- private final int[] values;
- private final int[] keys;
- private int size = 0;
-
- /**
- * Creates a new instance of {@code IndexMap} whose {@code nil()} method
- * will return the specified {@code nil} value.
- * @param maxKey the maximum key
- * @param nil the value that will be returned by the instance's
- * {@code get()} method if a key has no assigned value
- * @throws IllegalArgumentException if {@code maxKey < 0}
- */
- public IndexMap(int maxKey, int nil) {
- if (maxKey < 0) {
- throw new IllegalArgumentException(String.valueOf(maxKey));
- }
- this.nil = nil;
- this.values = new int[maxKey+1];
- this.keys = new int[maxKey+1];
- Arrays.fill(values, nil);
- }
-
- /**
- * Returns the value that is returned by {@code this.get()} if
- * a key has no assigned value.
- * @return the value that is returned by {@code this.get()} if
- * a key has no assigned value
- */
- public int nil() {
- return nil;
- }
-
- /**
- * Adds the specified key and value to the map. If the map
- * contains a value for the specified key when the method is invoked,
- * the old value is replaced by the specified value.
- *
- * @param key the key
- * @param value the value
- * @return the previous value associated with {@code key}, or
- * {@code this.nil()} if no such previous value exists
- *
- * @throws IllegalArgumentException if {@code value == this.nil()}
- * @throws IndexOutOfBoundsException if
- * {@code key < 0 || key > this.maxKey()}
- */
- public int put(int key, int value) {
- if (value==nil) {
- throw new IllegalArgumentException("value==nil()");
- }
- int prevValue = values[key];
- if (prevValue == nil) {
- keys[size++] = key;
- }
- this.values[key] = value;
- return prevValue;
- }
-
- /**
- * Returns the value associated with the specified key, or
- * {@code this.nil()} if the specified key is not contained in the map.
- * @param key the key
- * @return the value associated with the specified key, or
- * {@code this.nil()} if the specified key is not contained in the map.
- *
- * @throws IndexOutOfBoundsException if
- * {@code key < 0 || key > this.maxKey()}
- */
- public int get(int key) {
- return values[key];
- }
-
- /**
- * Returns the number of key-value pairs in the map.
- *
- * @return the number of key-value pairs in the map
- */
- public int size() {
- return size;
- }
-
- /**
- * Returns the maximum key.
- *
- * @return the maximum key
- */
- public int maxKey() {
- return keys.length-1;
- }
-
- /**
- * Removes all key-value pairs from the map.
- */
- public void clear() {
- for (int j=0, n=size; j<n; ++j) {
- values[keys[j]] = nil;
- }
- size = 0;
- }
-
- /**
- * Returns the specified key in an enumeration of the keys in the map.
- * @param index an index of an element in the enumeration
- * @return the specified key in an enumeration of the keys-value
- * pairs in the map
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public int enumeratedKey(int index) {
- if (index>=size) {
- throw new IndexOutOfBoundsException(String.valueOf(index));
- }
- return keys[index];
- }
-
- /**
- * Returns the value associated with the specified key
- * in an enumeration of the keys in the map.
- * If {@code (index >= 0 && index < this.size())}, then the returned value
- * will satisfy:
- * {@code this.get(this.enumeratedKey(index)==this.enumeratedValue(index)}.
- * @param index an index of an element in the enumeration
- * @return the value associated with the specified key
- * in an enumeration of the keys in the map
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public int enumeratedValue(int index) {
- if (index>=size) {
- throw new IndexOutOfBoundsException(String.valueOf(index));
- }
- return values[keys[index]];
- }
-
- /**
- * Returns a string representation of {@code this}. The exact
- * details of the representation are unspecified and subject to change.
- *
- * @return a string representation of {@code this}.
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(80);
- sb.append("size=");
- sb.append(size);
- sb.append(" {");
- for (int j=0; j<size; ++j) {
- sb.append(enumeratedKey(j));
- sb.append(" : ");
- sb.append(enumeratedValue(j));
- if (j+1 < size) {
- sb.append(Const.comma);
- }
- }
- sb.append("}");
- return sb.toString();
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package blbutil;
+
+import java.util.Arrays;
+
+/**
+ * <p>Class {@code IndexMap} is a map whose keys are a bounded set of
+ * non-negative integers and whose values are integers.
+ * </p>
+ * <p>Class {@code IndexMap} supports a {@code clear()} method, but it does not
+ * support a {@code remove()} method.
+ * </p>
+ * <p>Class {@code IndexMap} is not thread-safe.
+ * </p>
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class IndexMap {
+
+ private final int nil;
+ private final int[] values;
+ private final int[] keys;
+ private int size = 0;
+
+ /**
+ * Creates a new instance of {@code IndexMap} whose {@code nil()} method
+ * will return the specified {@code nil} value.
+ * @param maxKey the maximum key
+ * @param nil the value that will be returned by the instance's
+ * {@code get()} method if a key has no assigned value
+ * @throws IllegalArgumentException if {@code maxKey < 0}
+ */
+ public IndexMap(int maxKey, int nil) {
+ if (maxKey < 0) {
+ throw new IllegalArgumentException(String.valueOf(maxKey));
+ }
+ this.nil = nil;
+ this.values = new int[maxKey+1];
+ this.keys = new int[maxKey+1];
+ Arrays.fill(values, nil);
+ }
+
+ /**
+ * Returns the value that is returned by {@code this.get()} if
+ * a key has no assigned value.
+ * @return the value that is returned by {@code this.get()} if
+ * a key has no assigned value
+ */
+ public int nil() {
+ return nil;
+ }
+
+ /**
+ * Adds the specified key and value to the map. If the map
+ * contains a value for the specified key when the method is invoked,
+ * the old value is replaced by the specified value.
+ *
+ * @param key the key
+ * @param value the value
+ * @return the previous value associated with {@code key}, or
+ * {@code this.nil()} if no such previous value exists
+ *
+ * @throws IllegalArgumentException if {@code value == this.nil()}
+ * @throws IndexOutOfBoundsException if
+ * {@code key < 0 || key > this.maxKey()}
+ */
+ public int put(int key, int value) {
+ if (value==nil) {
+ throw new IllegalArgumentException("value==nil()");
+ }
+ int prevValue = values[key];
+ if (prevValue == nil) {
+ keys[size++] = key;
+ }
+ this.values[key] = value;
+ return prevValue;
+ }
+
+ /**
+ * Returns the value associated with the specified key, or
+ * {@code this.nil()} if the specified key is not contained in the map.
+ * @param key the key
+ * @return the value associated with the specified key, or
+ * {@code this.nil()} if the specified key is not contained in the map.
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code key < 0 || key > this.maxKey()}
+ */
+ public int get(int key) {
+ return values[key];
+ }
+
+ /**
+ * Returns the number of key-value pairs in the map.
+ *
+ * @return the number of key-value pairs in the map
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Returns the maximum key.
+ *
+ * @return the maximum key
+ */
+ public int maxKey() {
+ return keys.length-1;
+ }
+
+ /**
+ * Removes all key-value pairs from the map.
+ */
+ public void clear() {
+ for (int j=0, n=size; j<n; ++j) {
+ values[keys[j]] = nil;
+ }
+ size = 0;
+ }
+
+ /**
+ * Returns the specified key in an enumeration of the keys in the map.
+ * @param index an index of an element in the enumeration
+ * @return the specified key in an enumeration of the keys-value
+ * pairs in the map
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public int enumeratedKey(int index) {
+ if (index>=size) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ return keys[index];
+ }
+
+ /**
+ * Returns the value associated with the specified key
+ * in an enumeration of the keys in the map.
+ * If {@code (index >= 0 && index < this.size())}, then the returned value
+ * will satisfy:
+ * {@code this.get(this.enumeratedKey(index)==this.enumeratedValue(index)}.
+ * @param index an index of an element in the enumeration
+ * @return the value associated with the specified key
+ * in an enumeration of the keys in the map
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public int enumeratedValue(int index) {
+ if (index>=size) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ return values[keys[index]];
+ }
+
+ /**
+ * Returns a string representation of {@code this}. The exact
+ * details of the representation are unspecified and subject to change.
+ *
+ * @return a string representation of {@code this}.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(80);
+ sb.append("size=");
+ sb.append(size);
+ sb.append(" {");
+ for (int j=0; j<size; ++j) {
+ sb.append(enumeratedKey(j));
+ sb.append(" : ");
+ sb.append(enumeratedValue(j));
+ if (j+1 < size) {
+ sb.append(Const.comma);
+ }
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+}
diff --git a/blbutil/IndexSet.java b/blbutil/IndexSet.java
index f2076e8..f1cebb9 100644
--- a/blbutil/IndexSet.java
+++ b/blbutil/IndexSet.java
@@ -1,152 +1,152 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package blbutil;
-
-import java.util.Arrays;
-
-/**
- * <p>Class {@code IndexSet} is a set that stores non-negative indices that are
- * less than or equal to a specified maximum value.
- * </p>
- * <p>Class {@code IndexSet} supports a {@code clear()} method, but it does not
- * support a {@code remove()} method.
- * </p>
- * <p>Class {@code IndexSet} is not thread-safe.
- * </p>
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class IndexSet {
-
- private final boolean[] inSet;
- private final int[] indices;
- private int size = 0;
-
- /**
- * Creates a new instance of {@code IndexSet} that can contain
- * non-negative integer indices that are less than or equal to the specified
- * maximum value.
- *
- * @param max the maximum element that is permitted in the set.
- * @throws IllegalArgumentException if {@code max < 0}
- */
- public IndexSet(int max) {
- if (max < 0) {
- throw new IllegalArgumentException(String.valueOf(max));
- }
- this.inSet = new boolean[max+1];
- this.indices = new int[max+1];
- }
-
- /**
- * Adds the specified element to the set.
- *
- * @param element an element to add to this set.
- * @return {@code true} if the set was changed by the operation, and
- * {@code false} otherwise.
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index > this.maxPermittedIndex()}
- */
- public boolean add(int element) {
- if (inSet[element]==false) {
- indices[size++] = element;
- inSet[element]=true;
- return true;
- }
- else {
- return false;
- }
- }
-
- /**
- * Returns {@code true} if the set contains the specified element,
- * and returns {@code false} otherwise.
- * @param element an element
- * @return {@code true} if the set contains the specified element
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index > this.maxPermittedIndex()}
- */
- public boolean contains(int element) {
- return inSet[element];
- }
-
- /**
- * Returns the number of elements in this set.
- *
- * @return the number of elements in this set
- */
- public int size() {
- return size;
- }
-
- /**
- * Returns the maximum permitted element in the set.
- *
- * @return the maximum permitted element in the set
- */
- public int maxPermittedElement() {
- return indices.length-1;
- }
-
- /**
- * Removes all elements from the set.
- */
- public void clear() {
- for (int j=0, n=size; j<n; ++j) {
- inSet[indices[j]] = false;
- }
- size = 0;
- }
-
- /**
- * Returns the specified element in an enumeration of the elements in the
- * set.
- * @param enumIndex an index of an element in the enumeration
- * @return the specified element in an enumeration of the elements in the
- * set
- * @throws IndexOutOfBoundsException if
- * {@code enumIndex < 0 || enumIndex >= this.size()}
- */
- public int enumeratedValue(int enumIndex) {
- if (enumIndex>=size) {
- throw new IndexOutOfBoundsException(String.valueOf(enumIndex));
- }
- return indices[enumIndex];
- }
-
- /**
- * Returns an array containing the elements in this set.
- * @return an array containing the elements in this set
- */
- public int[] toArray() {
- return Arrays.copyOf(indices, size);
- }
-
- /**
- * Returns {@code java.util.Arrays.toString(this.toArray())}.
- *
- * @return {@code java.util.Arrays.toString(this.toArray())}
- */
- @Override
- public String toString() {
- return Arrays.toString(toArray());
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package blbutil;
+
+import java.util.Arrays;
+
+/**
+ * <p>Class {@code IndexSet} is a set that stores non-negative indices that are
+ * less than or equal to a specified maximum value.
+ * </p>
+ * <p>Class {@code IndexSet} supports a {@code clear()} method, but it does not
+ * support a {@code remove()} method.
+ * </p>
+ * <p>Class {@code IndexSet} is not thread-safe.
+ * </p>
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class IndexSet {
+
+ private final boolean[] inSet;
+ private final int[] indices;
+ private int size = 0;
+
+ /**
+ * Creates a new instance of {@code IndexSet} that can contain
+ * non-negative integer indices that are less than or equal to the specified
+ * maximum value.
+ *
+ * @param max the maximum element that is permitted in the set.
+ * @throws IllegalArgumentException if {@code max < 0}
+ */
+ public IndexSet(int max) {
+ if (max < 0) {
+ throw new IllegalArgumentException(String.valueOf(max));
+ }
+ this.inSet = new boolean[max+1];
+ this.indices = new int[max+1];
+ }
+
+ /**
+ * Adds the specified element to the set.
+ *
+ * @param element an element to add to this set.
+ * @return {@code true} if the set was changed by the operation, and
+ * {@code false} otherwise.
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index > this.maxPermittedIndex()}
+ */
+ public boolean add(int element) {
+ if (inSet[element]==false) {
+ indices[size++] = element;
+ inSet[element]=true;
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns {@code true} if the set contains the specified element,
+ * and returns {@code false} otherwise.
+ * @param element an element
+ * @return {@code true} if the set contains the specified element
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index > this.maxPermittedIndex()}
+ */
+ public boolean contains(int element) {
+ return inSet[element];
+ }
+
+ /**
+ * Returns the number of elements in this set.
+ *
+ * @return the number of elements in this set
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Returns the maximum permitted element in the set.
+ *
+ * @return the maximum permitted element in the set
+ */
+ public int maxPermittedElement() {
+ return indices.length-1;
+ }
+
+ /**
+ * Removes all elements from the set.
+ */
+ public void clear() {
+ for (int j=0, n=size; j<n; ++j) {
+ inSet[indices[j]] = false;
+ }
+ size = 0;
+ }
+
+ /**
+ * Returns the specified element in an enumeration of the elements in the
+ * set.
+ * @param enumIndex an index of an element in the enumeration
+ * @return the specified element in an enumeration of the elements in the
+ * set
+ * @throws IndexOutOfBoundsException if
+ * {@code enumIndex < 0 || enumIndex >= this.size()}
+ */
+ public int enumeratedValue(int enumIndex) {
+ if (enumIndex>=size) {
+ throw new IndexOutOfBoundsException(String.valueOf(enumIndex));
+ }
+ return indices[enumIndex];
+ }
+
+ /**
+ * Returns an array containing the elements in this set.
+ * @return an array containing the elements in this set
+ */
+ public int[] toArray() {
+ return Arrays.copyOf(indices, size);
+ }
+
+ /**
+ * Returns {@code java.util.Arrays.toString(this.toArray())}.
+ *
+ * @return {@code java.util.Arrays.toString(this.toArray())}
+ */
+ @Override
+ public String toString() {
+ return Arrays.toString(toArray());
+ }
+}
diff --git a/blbutil/InputIt.java b/blbutil/InputIt.java
index ae2b633..eb34f31 100644
--- a/blbutil/InputIt.java
+++ b/blbutil/InputIt.java
@@ -1,320 +1,320 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package blbutil;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.NoSuchElementException;
-import java.util.zip.GZIPInputStream;
-import net.sf.samtools.util.BlockCompressedInputStream;
-
-/**
- * <p>Class {@code InputIt} is a buffered iterator whose {@code next()}
- * method returns lines of a text input stream.
- * </p>
- * <p>If an {@code IOException} is thrown when an {@code InputIt}
- * instance reads from the text input stream, the {@code IOException}
- * is trapped, an error message is written to standard out, and the
- * Java Virtual Machine is terminated.
- * </p>
- * Instances of class {@code InputIt} are not thread-safe.
- *
- * @see #DEFAULT_BUFFER_SIZE
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class InputIt implements FileIt<String> {
-
- /**
- * The default buffer size, which is 4,194,304 bytes.
- */
- public static final int DEFAULT_BUFFER_SIZE = 1<<22;
-
- private final File file;
- private final BufferedReader in;
- private String next = null;
-
- /**
- * Constructs a new {@code InputStreamIterator} with default buffer
- * size that will iterate through lines of the specified input stream.
- *
- * @param is input stream of text data
- *
- * @see #DEFAULT_BUFFER_SIZE
- */
- private InputIt(InputStream is, File file) {
- this(is, file, DEFAULT_BUFFER_SIZE);
- }
-
- /**
- * Constructs a new {@code InputStreamIterator} with default buffer size
- * that will iterate through the lines of the specified input stream.
- *
- * @param is input stream of text data
- * @param bufferSize the buffer size in bytes
- *
- * @throws IllegalArgumentException if {@code bufferSize < 0}
- */
- private InputIt(InputStream is, File file, int bufferSize) {
- BufferedReader br = null;
- try {
- InputStreamReader isr = new InputStreamReader(is);
- br = new BufferedReader(isr, bufferSize);
- next = br.readLine();
- }
- catch(IOException e) {
- Utilities.exit("Error reading " + is, e);
- }
- this.in = br;
- this.file = file;
- }
-
- @Override
- public File file() {
- return file;
- }
-
- /**
- * Returns {@code true} if the iteration has more elements.
- * @return {@code true} if the iteration has more elements
- */
- @Override
- public boolean hasNext() {
- return (next != null);
- }
-
- /**
- * Returns the next element in the iteration.
- * @return the next element in the iteration
- * @throws NoSuchElementException if the iteration has no more elements
- */
- @Override
- public String next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
- String current = next;
- try {
- next = in.readLine();
- }
- catch (IOException e) {
- Utilities.exit("Error reading " + in, e);
- }
- return current;
- }
-
- /**
- * The {@code remove} method is not supported by this iterator.
- * @throws UnsupportedOperationException if this method is invoked
- */
- @Override
- public void remove() {
- String s = this.getClass().toString() + ".remove()";
- throw new UnsupportedOperationException(s);
- }
-
- @Override
- public void close() {
- try {
- in.close();
- }
- catch (IOException e) {
- Utilities.exit("Error closing " + in, e);
- }
- next=null;
- }
-
- /**
- * Returns a string representation of this iterator. The exact details
- * of the representation are unspecified and subject to change.
- * @return a string representation of this iterator
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(200);
- sb.append("[file= ");
- sb.append(file);
- sb.append("; next=\"");
- sb.append(next);
- sb.append("\"]");
- return sb.toString();
- }
-
- /**
- * Constructs and returns an {@code InputIt} instance with the default
- * buffer size that iterates through lines of text read from standard input.
- *
- * @return a new {@code InputIt} instance that iterates
- * through lines of text read from standard input
- */
- public static InputIt fromStdIn() {
- File file = null;
- return new InputIt(System.in, file);
- }
-
- /**
- * Constructs and returns an {@code InputIt} instance with the specified
- * buffer size that iterates through lines of text read from standard input.
- *
- * @param bufferSize the buffer size in bytes
- *
- * @return a new {@code InputIt} instance that iterates
- * through lines of text read from standard input
- *
- * @throws IllegalArgumentException if {@code bufferSize < 0}
- */
- public static InputIt fromStdIn(int bufferSize) {
- File file = null;
- return new InputIt(System.in, file, bufferSize);
- }
-
- /**
- * Constructs and returns an {@code InputIt} instance with the default
- * buffer size that iterates through lines of the specified compressed
- * or uncompressed text file. If the filename ends in ".gz", the file
- * must be either BGZIP-compressed or GZIP-compressed.
- *
- * @param file a compressed or uncompressed text file
- * @return a new {@code InputIt} instance that iterates
- * through lines of the specified text file
- *
- * @throws NullPointerException if {@code file == null}
- */
- public static InputIt fromGzipFile(File file) {
- try {
- InputStream is = new FileInputStream(file);
- if (file.getName().endsWith(".gz")) {
- if (isBGZipFile(file)) {
- return new InputIt(
- new BlockCompressedInputStream(is), file);
- }
- else {
- return new InputIt(new GZIPInputStream(is), file);
- }
- }
- else {
- return new InputIt(is, file);
- }
- }
- catch(FileNotFoundException e) {
- Utilities.exit("Error opening " + file, e);
- }
- catch(IOException e) {
- Utilities.exit("Error reading " + file, e);
- }
- assert false;
- return null;
- }
-
- /**
- * Constructs and returns an {@code InputIt} instance with the specified
- * buffer size that iterates through lines of the specified compressed
- * or uncompressed text file. If the filename ends in ".gz", the file must
- * be either BGZIP-compressed or GZIP-compressed.
- *
- * @param file a compressed or uncompressed text file
- * @param bufferSize the buffer size in bytes
- * @return a new {@code InputIt} instance that iterates
- * through lines of the specified text file
- *
- * @throws IllegalArgumentException if {@code bufferSize < 0}
- * @throws NullPointerException if {@code file == null}
- */
- public static InputIt fromGzipFile(File file, int bufferSize) {
- try {
- InputStream is = new FileInputStream(file);
- if (file.getName().endsWith(".gz")) {
- if (isBGZipFile(file)) {
- return new InputIt(
- new BlockCompressedInputStream(is), file, bufferSize);
- }
- else {
- return new InputIt(new GZIPInputStream(is), file, bufferSize);
- }
- }
- else {
- return new InputIt(is, file);
- }
- }
- catch(FileNotFoundException e) {
- Utilities.exit("Error opening " + file, e);
- }
- catch(IOException e) {
- Utilities.exit("Error reading " + file, e);
- }
- assert false;
- return null;
- }
-
- private static boolean isBGZipFile(File file) throws IOException {
- try (InputStream is=new BufferedInputStream(new FileInputStream(file))) {
- return BlockCompressedInputStream.isValidFile(is);
- }
- }
-
- /**
- * Constructs and returns an {@code InputIt} instance with the default
- * buffer size that iterates through lines of the specified text file.
- *
- * @param file a text file
- * @return a new {@code InputIt} instance that iterates through
- * lines of the specified text file
- *
- * @throws NullPointerException if {@code filename == null}
- */
- public static InputIt fromTextFile(File file) {
- try {
- return new InputIt(new FileInputStream(file), file);
- }
- catch(FileNotFoundException e) {
- Utilities.exit("Error opening " + file, e);
- }
- assert false;
- return null;
- }
-
- /**
- * Constructs and returns an {@code InputIt} instance with the specified
- * buffer size that iterates through lines of the specified text file.
- *
- * @param file a text file
- * @param bufferSize the buffer size in bytes
- * @return a new {@code InputIt} instance that iterates through
- * lines of the specified text file
- *
- * @throws IllegalArgumentException if {@code bufferSize < 0}
- * @throws NullPointerException if {@code filename == null}
- */
- public static InputIt fromTextFile(File file, int bufferSize) {
- try {
- return new InputIt(new FileInputStream(file), file, bufferSize);
- }
- catch(FileNotFoundException e) {
- Utilities.exit("Error opening " + file, e);
- }
- assert false;
- return null;
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package blbutil;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.NoSuchElementException;
+import java.util.zip.GZIPInputStream;
+import net.sf.samtools.util.BlockCompressedInputStream;
+
+/**
+ * <p>Class {@code InputIt} is a buffered iterator whose {@code next()}
+ * method returns lines of a text input stream.
+ * </p>
+ * <p>If an {@code IOException} is thrown when an {@code InputIt}
+ * instance reads from the text input stream, the {@code IOException}
+ * is trapped, an error message is written to standard out, and the
+ * Java Virtual Machine is terminated.
+ * </p>
+ * Instances of class {@code InputIt} are not thread-safe.
+ *
+ * @see #DEFAULT_BUFFER_SIZE
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class InputIt implements FileIt<String> {
+
+ /**
+ * The default buffer size, which is 4,194,304 bytes.
+ */
+ public static final int DEFAULT_BUFFER_SIZE = 1<<22;
+
+ private final File file;
+ private final BufferedReader in;
+ private String next = null;
+
+ /**
+ * Constructs a new {@code InputStreamIterator} with default buffer
+ * size that will iterate through lines of the specified input stream.
+ *
+ * @param is input stream of text data
+ *
+ * @see #DEFAULT_BUFFER_SIZE
+ */
+ private InputIt(InputStream is, File file) {
+ this(is, file, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Constructs a new {@code InputStreamIterator} with default buffer size
+ * that will iterate through the lines of the specified input stream.
+ *
+ * @param is input stream of text data
+ * @param bufferSize the buffer size in bytes
+ *
+ * @throws IllegalArgumentException if {@code bufferSize < 0}
+ */
+ private InputIt(InputStream is, File file, int bufferSize) {
+ BufferedReader br = null;
+ try {
+ InputStreamReader isr = new InputStreamReader(is);
+ br = new BufferedReader(isr, bufferSize);
+ next = br.readLine();
+ }
+ catch(IOException e) {
+ Utilities.exit("Error reading " + is, e);
+ }
+ this.in = br;
+ this.file = file;
+ }
+
+ @Override
+ public File file() {
+ return file;
+ }
+
+ /**
+ * Returns {@code true} if the iteration has more elements.
+ * @return {@code true} if the iteration has more elements
+ */
+ @Override
+ public boolean hasNext() {
+ return (next != null);
+ }
+
+ /**
+ * Returns the next element in the iteration.
+ * @return the next element in the iteration
+ * @throws NoSuchElementException if the iteration has no more elements
+ */
+ @Override
+ public String next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ String current = next;
+ try {
+ next = in.readLine();
+ }
+ catch (IOException e) {
+ Utilities.exit("Error reading " + in, e);
+ }
+ return current;
+ }
+
+ /**
+ * The {@code remove} method is not supported by this iterator.
+ * @throws UnsupportedOperationException if this method is invoked
+ */
+ @Override
+ public void remove() {
+ String s = this.getClass().toString() + ".remove()";
+ throw new UnsupportedOperationException(s);
+ }
+
+ @Override
+ public void close() {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ Utilities.exit("Error closing " + in, e);
+ }
+ next=null;
+ }
+
+ /**
+ * Returns a string representation of this iterator. The exact details
+ * of the representation are unspecified and subject to change.
+ * @return a string representation of this iterator
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(200);
+ sb.append("[file= ");
+ sb.append(file);
+ sb.append("; next=\"");
+ sb.append(next);
+ sb.append("\"]");
+ return sb.toString();
+ }
+
+ /**
+ * Constructs and returns an {@code InputIt} instance with the default
+ * buffer size that iterates through lines of text read from standard input.
+ *
+ * @return a new {@code InputIt} instance that iterates
+ * through lines of text read from standard input
+ */
+ public static InputIt fromStdIn() {
+ File file = null;
+ return new InputIt(System.in, file);
+ }
+
+ /**
+ * Constructs and returns an {@code InputIt} instance with the specified
+ * buffer size that iterates through lines of text read from standard input.
+ *
+ * @param bufferSize the buffer size in bytes
+ *
+ * @return a new {@code InputIt} instance that iterates
+ * through lines of text read from standard input
+ *
+ * @throws IllegalArgumentException if {@code bufferSize < 0}
+ */
+ public static InputIt fromStdIn(int bufferSize) {
+ File file = null;
+ return new InputIt(System.in, file, bufferSize);
+ }
+
+ /**
+ * Constructs and returns an {@code InputIt} instance with the default
+ * buffer size that iterates through lines of the specified compressed
+ * or uncompressed text file. If the filename ends in ".gz", the file
+ * must be either BGZIP-compressed or GZIP-compressed.
+ *
+ * @param file a compressed or uncompressed text file
+ * @return a new {@code InputIt} instance that iterates
+ * through lines of the specified text file
+ *
+ * @throws NullPointerException if {@code file == null}
+ */
+ public static InputIt fromGzipFile(File file) {
+ try {
+ InputStream is = new FileInputStream(file);
+ if (file.getName().endsWith(".gz")) {
+ if (isBGZipFile(file)) {
+ return new InputIt(
+ new BlockCompressedInputStream(is), file);
+ }
+ else {
+ return new InputIt(new GZIPInputStream(is), file);
+ }
+ }
+ else {
+ return new InputIt(is, file);
+ }
+ }
+ catch(FileNotFoundException e) {
+ Utilities.exit("Error opening " + file, e);
+ }
+ catch(IOException e) {
+ Utilities.exit("Error reading " + file, e);
+ }
+ assert false;
+ return null;
+ }
+
+ /**
+ * Constructs and returns an {@code InputIt} instance with the specified
+ * buffer size that iterates through lines of the specified compressed
+ * or uncompressed text file. If the filename ends in ".gz", the file must
+ * be either BGZIP-compressed or GZIP-compressed.
+ *
+ * @param file a compressed or uncompressed text file
+ * @param bufferSize the buffer size in bytes
+ * @return a new {@code InputIt} instance that iterates
+ * through lines of the specified text file
+ *
+ * @throws IllegalArgumentException if {@code bufferSize < 0}
+ * @throws NullPointerException if {@code file == null}
+ */
+ public static InputIt fromGzipFile(File file, int bufferSize) {
+ try {
+ InputStream is = new FileInputStream(file);
+ if (file.getName().endsWith(".gz")) {
+ if (isBGZipFile(file)) {
+ return new InputIt(
+ new BlockCompressedInputStream(is), file, bufferSize);
+ }
+ else {
+ return new InputIt(new GZIPInputStream(is), file, bufferSize);
+ }
+ }
+ else {
+ return new InputIt(is, file);
+ }
+ }
+ catch(FileNotFoundException e) {
+ Utilities.exit("Error opening " + file, e);
+ }
+ catch(IOException e) {
+ Utilities.exit("Error reading " + file, e);
+ }
+ assert false;
+ return null;
+ }
+
+ private static boolean isBGZipFile(File file) throws IOException {
+ try (InputStream is=new BufferedInputStream(new FileInputStream(file))) {
+ return BlockCompressedInputStream.isValidFile(is);
+ }
+ }
+
+ /**
+ * Constructs and returns an {@code InputIt} instance with the default
+ * buffer size that iterates through lines of the specified text file.
+ *
+ * @param file a text file
+ * @return a new {@code InputIt} instance that iterates through
+ * lines of the specified text file
+ *
+ * @throws NullPointerException if {@code filename == null}
+ */
+ public static InputIt fromTextFile(File file) {
+ try {
+ return new InputIt(new FileInputStream(file), file);
+ }
+ catch(FileNotFoundException e) {
+ Utilities.exit("Error opening " + file, e);
+ }
+ assert false;
+ return null;
+ }
+
+ /**
+ * Constructs and returns an {@code InputIt} instance with the specified
+ * buffer size that iterates through lines of the specified text file.
+ *
+ * @param file a text file
+ * @param bufferSize the buffer size in bytes
+ * @return a new {@code InputIt} instance that iterates through
+ * lines of the specified text file
+ *
+ * @throws IllegalArgumentException if {@code bufferSize < 0}
+ * @throws NullPointerException if {@code filename == null}
+ */
+ public static InputIt fromTextFile(File file, int bufferSize) {
+ try {
+ return new InputIt(new FileInputStream(file), file, bufferSize);
+ }
+ catch(FileNotFoundException e) {
+ Utilities.exit("Error opening " + file, e);
+ }
+ assert false;
+ return null;
+ }
+}
diff --git a/blbutil/StringUtil.java b/blbutil/StringUtil.java
index 09632bd..7c81666 100644
--- a/blbutil/StringUtil.java
+++ b/blbutil/StringUtil.java
@@ -1,281 +1,281 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package blbutil;
-
-/**
- * Class {@code StringUtil} is a utility class with static methods
- * for counting and returning delimited fields in a string.
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class StringUtil {
-
- /* Private constructor to prevent instantiation */
- private StringUtil() {
- }
-
- /**
- * Returns the number of delimited fields in the specified
- * string. Returns 0 if the specified string has length 0.
- *
- * @param s a string
- * @param delimiter a delimiter character
- * @return the number of delimited fields in the specified string
- * @throws NullPointerException if {@code s == null}
- */
- public static int countFields(String s, char delimiter) {
- int cnt = 0;
- for (int j=0, n=s.length(); j<n; ++j) {
- if (s.charAt(j)==delimiter) {
- ++cnt;
- }
- }
- return cnt + 1;
- }
-
- /**
- * Returns {@code Math.min(countFields(s, delimiter), max)}.
- *
- * @param s a string with 0 or more {@code delimiter} characters
- * @param delimiter the delimiter character
- * @param max the maximum value that can be returned
- *
- * @return {@code Math.min(countFields(s, delimiter), max)}
- *
- * @throws NullPointerException if {@code s == null}
- */
- public static int countFields(String s, char delimiter, int max) {
- int cnt = 0;
- int maxCnt = max - 1;
- for (int j=0, n=s.length(); j<n && cnt<maxCnt; ++j) {
- if (s.charAt(j)==delimiter) {
- ++cnt;
- }
- }
- return Math.min(cnt + 1, max);
- }
-
- /**
- * Returns an array obtained by splitting the specified string
- * around the specified delimiter.
- * The array returned by this method contains each substring of
- * the string that does not contain the delimiter and that
- * is preceded by the delimiter or the beginning of
- * the string and that is terminated by the delimiter or the end
- * of the string. The substrings in the array are in
- * the order in which they occur in the specified string.
- * If there are no delimiters in the specified string then the method
- * return an array of length one, whose single element is the specified
- * string.
- *
- * @param s a string
- * @param delimiter a delimiter character
- *
- * @return the array of strings obtained by splitting the specified string
- * around the specified delimiter
- *
- * @throws NullPointerException if {@code s == null}
- */
- public static String[] getFields(String s, char delimiter) {
- String[] fields = new String[countFields(s, delimiter)];
- int start = 0;
- for (int j=0; j<fields.length; ++j) {
- int end = s.indexOf(delimiter, start);
- fields[j] = end>=0 ? s.substring(start,end) : s.substring(start);
- start = end + 1;
- }
- return fields;
- }
-
- /**
- * Returns an array obtained by splitting the specified string
- * around the first {@code (limit - 1)} occurrences of the specified
- * delimiter. If the string contains fewer than {@code (limit - 1)}
- * delimiter characters, the returned value will equal
- * {@code StringUtil.getFields(s, delimiter)}
- *
- * @param s a string
- * @param delimiter a delimiter character
- * @param limit the maximum length of the returned array
- *
- * @return an array obtained by splitting the specified string
- * around the specified delimiter
- *
- * @throws NullPointerException if {@code s == null}
- * @throws IllegalArgumentException if {@code limit < 2 }
- */
- public static String[] getFields(String s, char delimiter, int limit) {
- if (limit < 2) {
- throw new IllegalArgumentException("limit: " + limit);
- }
- String[] fields = new String[countFields(s, delimiter, limit)];
- if (fields.length > 0) {
- int start = 0;
- for (int j=0, n=fields.length-1; j<n; ++j) {
- int end = s.indexOf(delimiter, start);
- fields[j] = s.substring(start, end);
- start = end + 1;
- }
- fields[fields.length - 1] = s.substring(start);
- }
- return fields;
- }
-
- /**
- * Returns the number of white-space delimited fields in the specified
- * string. A field is a maximal set of consecutive characters that are not
- * white space characters. White space is defined as any unicode
- * characters less than or equal to '\u0020'.
- *
- * @param s a string
- * @return the number of white-space delimited fields in the specified
- * string
- * @throws NullPointerException if {@code s == null}
- */
- public static int countFields(String s) {
- int start = 0;
- int end = s.length();
- while (start<end && s.charAt(start)<=' ') {
- ++start;
- }
- while (end>start && s.charAt(end-1)<=' ') {
- --end;
- }
- int fieldCount = (start<end) ? 1 : 0;
- while (++start<end && s.charAt(start)>' ') {
- }
- while (start<end) {
- while (s.charAt(++start)<=' ') {
- }
- ++fieldCount;
- while (++start<end && s.charAt(start)>' ') {
- }
- }
- return fieldCount;
- }
-
- /**
- * Returns an array obtained by trimming white-space from the
- * beginning and end of the specified string, and splitting the resulting
- * string around white space.
- * White space is any maximal substring of unicode characters
- * less than or equal to '\u0020'. White-space at the beginning and
- * end of the string is ignored. The substrings in the returned array
- * are in the order in which they occur in this string. If there is no
- * white-space in the specified string, the method returns an array
- * of length one whose single element is the trimmed string. If the
- * specified string contains only white-space a string array
- * of length 0 is returned.
- *
- * @param s a string
- * @return the array of strings obtained by splitting the specified string
- * around white space
- *
- * @throws NullPointerException if {@code s == null}
- */
- public static String[] getFields(String s) {
- s = s.trim();
- int n = s.length();
- String[] fields = new String[countFields(s)];
- if (fields.length > 0) {
- int index = 0;
- int start = 0;
- int j = -1;
- while (++j<n && s.charAt(j)>' ') {
- }
- fields[index++] = s.substring(start, j);
- while (j<n) {
- while (s.charAt(++j)<=' ') {
- }
- start = j;
- while (++j<n && s.charAt(j)>' ') {
- }
- fields[index++] = s.substring(start, j);
- }
- assert index==fields.length;
- }
- return fields;
- }
-
- /**
- * <p>Returns an array obtained by trimming white-space from the
- * beginning and end of the specified string, and splitting the resulting
- * string around the first {@code (limit-1)} white-space delimiters.
- * A white-space delimiter is any maximal substring of unicode characters
- * less than or equal to '\u0020'. If the trimemed string contains
- * fewer than {@code (limit - 1)} white space delimiters, the returned value
- * will equal {@code StringUtil.getFields(s)}. The substrings in the
- * returned array are in the order in which they occur in this string.
- * If there are no white-space delimiters in the specified string, the
- * method returns an array of length one whose single element is the
- * trimmed string. If the specified string contains only white-space,
- * a string array of length 0 is returned.
- *</p>
- *
- * @param s a string
- * @param limit the maximum length of the returned array
- *
- * @return the array of strings obtained by splitting the specified string
- * around white space
- *
- * @throws NullPointerException if {@code s == null}
- * @throws IllegalArgumentException if {@code limit < 2}
- */
- public static String[] getFields(String s, int limit) {
- if (limit<2) {
- throw new IllegalArgumentException("limit: " + limit);
- }
- s = s.trim();
- int n = s.length();
- int j=-1;
- while (++j<n && s.charAt(j)>' ') {
- }
- int fieldCount = (j>0) ? 1 : 0;
- while (j<n && fieldCount<limit) {
- while (s.charAt(++j)<=' ') {
- }
- ++fieldCount;
- while (++j<n && s.charAt(j)>' ') {
- }
- }
- String[] fields = new String[fieldCount];
- if (fields.length>0) {
- int index = 0;
- int start = 0;
- j = -1;
- while (++j<n && s.charAt(j)>' ') {
- }
- fields[index++] = s.substring(start, j);
- while (j<n && index<limit) {
- while (s.charAt(++j)<=' ') {
- }
- start = j;
- while (++j<n && s.charAt(j)>' ') {
- }
- if (index < limit-1) {
- fields[index++] = s.substring(start, j);
- }
- else {
- fields[index++] = s.substring(start);
- }
- }
- }
- return fields;
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package blbutil;
+
+/**
+ * Class {@code StringUtil} is a utility class with static methods
+ * for counting and returning delimited fields in a string.
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class StringUtil {
+
+ /* Private constructor to prevent instantiation */
+ private StringUtil() {
+ }
+
+ /**
+ * Returns the number of delimited fields in the specified
+ * string. Returns 0 if the specified string has length 0.
+ *
+ * @param s a string
+ * @param delimiter a delimiter character
+ * @return the number of delimited fields in the specified string
+ * @throws NullPointerException if {@code s == null}
+ */
+ public static int countFields(String s, char delimiter) {
+ int cnt = 0;
+ for (int j=0, n=s.length(); j<n; ++j) {
+ if (s.charAt(j)==delimiter) {
+ ++cnt;
+ }
+ }
+ return cnt + 1;
+ }
+
+ /**
+ * Returns {@code Math.min(countFields(s, delimiter), max)}.
+ *
+ * @param s a string with 0 or more {@code delimiter} characters
+ * @param delimiter the delimiter character
+ * @param max the maximum value that can be returned
+ *
+ * @return {@code Math.min(countFields(s, delimiter), max)}
+ *
+ * @throws NullPointerException if {@code s == null}
+ */
+ public static int countFields(String s, char delimiter, int max) {
+ int cnt = 0;
+ int maxCnt = max - 1;
+ for (int j=0, n=s.length(); j<n && cnt<maxCnt; ++j) {
+ if (s.charAt(j)==delimiter) {
+ ++cnt;
+ }
+ }
+ return Math.min(cnt + 1, max);
+ }
+
+ /**
+ * Returns an array obtained by splitting the specified string
+ * around the specified delimiter.
+ * The array returned by this method contains each substring of
+ * the string that does not contain the delimiter and that
+ * is preceded by the delimiter or the beginning of
+ * the string and that is terminated by the delimiter or the end
+ * of the string. The substrings in the array are in
+ * the order in which they occur in the specified string.
+ * If there are no delimiters in the specified string then the method
+ * return an array of length one, whose single element is the specified
+ * string.
+ *
+ * @param s a string
+ * @param delimiter a delimiter character
+ *
+ * @return the array of strings obtained by splitting the specified string
+ * around the specified delimiter
+ *
+ * @throws NullPointerException if {@code s == null}
+ */
+ public static String[] getFields(String s, char delimiter) {
+ String[] fields = new String[countFields(s, delimiter)];
+ int start = 0;
+ for (int j=0; j<fields.length; ++j) {
+ int end = s.indexOf(delimiter, start);
+ fields[j] = end>=0 ? s.substring(start,end) : s.substring(start);
+ start = end + 1;
+ }
+ return fields;
+ }
+
+ /**
+ * Returns an array obtained by splitting the specified string
+ * around the first {@code (limit - 1)} occurrences of the specified
+ * delimiter. If the string contains fewer than {@code (limit - 1)}
+ * delimiter characters, the returned value will equal
+ * {@code StringUtil.getFields(s, delimiter)}
+ *
+ * @param s a string
+ * @param delimiter a delimiter character
+ * @param limit the maximum length of the returned array
+ *
+ * @return an array obtained by splitting the specified string
+ * around the specified delimiter
+ *
+ * @throws NullPointerException if {@code s == null}
+ * @throws IllegalArgumentException if {@code limit < 2 }
+ */
+ public static String[] getFields(String s, char delimiter, int limit) {
+ if (limit < 2) {
+ throw new IllegalArgumentException("limit: " + limit);
+ }
+ String[] fields = new String[countFields(s, delimiter, limit)];
+ if (fields.length > 0) {
+ int start = 0;
+ for (int j=0, n=fields.length-1; j<n; ++j) {
+ int end = s.indexOf(delimiter, start);
+ fields[j] = s.substring(start, end);
+ start = end + 1;
+ }
+ fields[fields.length - 1] = s.substring(start);
+ }
+ return fields;
+ }
+
+ /**
+ * Returns the number of white-space delimited fields in the specified
+ * string. A field is a maximal set of consecutive characters that are not
+ * white space characters. White space is defined as any unicode
+ * characters less than or equal to '\u0020'.
+ *
+ * @param s a string
+ * @return the number of white-space delimited fields in the specified
+ * string
+ * @throws NullPointerException if {@code s == null}
+ */
+ public static int countFields(String s) {
+ int start = 0;
+ int end = s.length();
+ while (start<end && s.charAt(start)<=' ') {
+ ++start;
+ }
+ while (end>start && s.charAt(end-1)<=' ') {
+ --end;
+ }
+ int fieldCount = (start<end) ? 1 : 0;
+ while (++start<end && s.charAt(start)>' ') {
+ }
+ while (start<end) {
+ while (s.charAt(++start)<=' ') {
+ }
+ ++fieldCount;
+ while (++start<end && s.charAt(start)>' ') {
+ }
+ }
+ return fieldCount;
+ }
+
+ /**
+ * Returns an array obtained by trimming white-space from the
+ * beginning and end of the specified string, and splitting the resulting
+ * string around white space.
+ * White space is any maximal substring of unicode characters
+ * less than or equal to '\u0020'. White-space at the beginning and
+ * end of the string is ignored. The substrings in the returned array
+ * are in the order in which they occur in this string. If there is no
+ * white-space in the specified string, the method returns an array
+ * of length one whose single element is the trimmed string. If the
+ * specified string contains only white-space a string array
+ * of length 0 is returned.
+ *
+ * @param s a string
+ * @return the array of strings obtained by splitting the specified string
+ * around white space
+ *
+ * @throws NullPointerException if {@code s == null}
+ */
+ public static String[] getFields(String s) {
+ s = s.trim();
+ int n = s.length();
+ String[] fields = new String[countFields(s)];
+ if (fields.length > 0) {
+ int index = 0;
+ int start = 0;
+ int j = -1;
+ while (++j<n && s.charAt(j)>' ') {
+ }
+ fields[index++] = s.substring(start, j);
+ while (j<n) {
+ while (s.charAt(++j)<=' ') {
+ }
+ start = j;
+ while (++j<n && s.charAt(j)>' ') {
+ }
+ fields[index++] = s.substring(start, j);
+ }
+ assert index==fields.length;
+ }
+ return fields;
+ }
+
+ /**
+ * <p>Returns an array obtained by trimming white-space from the
+ * beginning and end of the specified string, and splitting the resulting
+ * string around the first {@code (limit-1)} white-space delimiters.
+ * A white-space delimiter is any maximal substring of unicode characters
+ * less than or equal to '\u0020'. If the trimemed string contains
+ * fewer than {@code (limit - 1)} white space delimiters, the returned value
+ * will equal {@code StringUtil.getFields(s)}. The substrings in the
+ * returned array are in the order in which they occur in this string.
+ * If there are no white-space delimiters in the specified string, the
+ * method returns an array of length one whose single element is the
+ * trimmed string. If the specified string contains only white-space,
+ * a string array of length 0 is returned.
+ *</p>
+ *
+ * @param s a string
+ * @param limit the maximum length of the returned array
+ *
+ * @return the array of strings obtained by splitting the specified string
+ * around white space
+ *
+ * @throws NullPointerException if {@code s == null}
+ * @throws IllegalArgumentException if {@code limit < 2}
+ */
+ public static String[] getFields(String s, int limit) {
+ if (limit<2) {
+ throw new IllegalArgumentException("limit: " + limit);
+ }
+ s = s.trim();
+ int n = s.length();
+ int j=-1;
+ while (++j<n && s.charAt(j)>' ') {
+ }
+ int fieldCount = (j>0) ? 1 : 0;
+ while (j<n && fieldCount<limit) {
+ while (s.charAt(++j)<=' ') {
+ }
+ ++fieldCount;
+ while (++j<n && s.charAt(j)>' ') {
+ }
+ }
+ String[] fields = new String[fieldCount];
+ if (fields.length>0) {
+ int index = 0;
+ int start = 0;
+ j = -1;
+ while (++j<n && s.charAt(j)>' ') {
+ }
+ fields[index++] = s.substring(start, j);
+ while (j<n && index<limit) {
+ while (s.charAt(++j)<=' ') {
+ }
+ start = j;
+ while (++j<n && s.charAt(j)>' ') {
+ }
+ if (index < limit-1) {
+ fields[index++] = s.substring(start, j);
+ }
+ else {
+ fields[index++] = s.substring(start);
+ }
+ }
+ }
+ return fields;
+ }
+}
diff --git a/blbutil/Utilities.java b/blbutil/Utilities.java
index bee3ff3..b62a575 100644
--- a/blbutil/Utilities.java
+++ b/blbutil/Utilities.java
@@ -1,208 +1,208 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package blbutil;
-
-import java.io.File;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Class {@code Utilities} contains miscellaneous static utility methods.
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class Utilities {
-
- private Utilities() {
- // private constructor to prevent instantiation
- }
-
- /**
- * Prints a summary of memory use at the time of method invocation
- * to standard output.
- * @param msg a string a message to be printed with the summary
- * of memory use
- */
- public static void printMemoryUse(String msg) {
- long Mb = 1024*1024;
- Runtime rt = Runtime.getRuntime();
- System.out.println(Const.nl + msg
- + Const.tab + "maxMb=" + (rt.maxMemory()/Mb)
- + Const.tab + "totalMb=" + (rt.totalMemory()/Mb)
- + Const.tab + "freeMb=" + (rt.freeMemory()/Mb)
- + Const.tab + "usedMb=" + ((rt.totalMemory() - rt.freeMemory())/Mb));
- }
-
- /**
- * Returns the current local time as a string. The
- * exact details of the string representation
- * are unspecified and subject to change.
- *
- * @return the current local time as a string.
- */
- public static String timeStamp() {
- Date now = new Date();
- SimpleDateFormat sdf =
- new SimpleDateFormat("hh:mm a z 'on' dd MMM yyyy");
- return sdf.format(now);
- }
-
- /**
- * <p>Returns a set of identifiers found in a text file that has
- * one identifier per line. The empty set is returned if
- * {@code file == null}. Blank lines are ignored, and white-space that
- * begins or ends a line is ignored.
- * </p>
- * If an {@code IOException} is thrown, an error message is printed
- * to standard error and the Java virtual machine is forced to terminate.
- *
- * @param file a text file with one identifier per line
- * @return a set of identifiers
- *
- * @throws IllegalArgumentException if the specified file does not exist
- * @throws IllegalArgumentException if the specified file is a directory
- * @throws IllegalArgumentException if any line of the specified
- * file contains two non-white-space characters separated by one or
- * more white-space characters
- */
- public static Set<String> idSet(File file) {
- if (file==null) {
- return Collections.emptySet();
- }
- else {
- if (file.exists()==false) {
- String s = "file does not exist: " + file;
- throw new IllegalArgumentException(s);
- }
- if (file.isDirectory()) {
- String s = "file is a directory: " + file;
- throw new IllegalArgumentException(s);
- }
- Set<String> idSet = new HashSet<>();
- try (FileIt<String> it = InputIt.fromGzipFile(file)) {
- while (it.hasNext()) {
- String line = it.next().trim();
- if (line.length() > 0) {
- if (StringUtil.countFields(line) > 1) {
- String s = "line has >1 white-space delimited fields: "
- + line;
- throw new IllegalArgumentException(s);
- }
- idSet.add(line);
- }
- }
- }
- return idSet;
- }
- }
-
- /**
- * Prints the specified string to the specified {@code PrintWriter} and
- * to standard out. The line separator string is not appended to the
- * specified string before printing.
- *
- * @param out a print writer
- * @param s a string to be printed
- *
- * @throws NullPointerException if {@code out == null}
- */
- public static void duoPrint(PrintWriter out, String s) {
- System.out.print(s);
- out.print(s);
- }
-
- /**
- * Prints the specified string to the specified {@code PrintWriter} and
- * to standard out. The line separator string is appended to the
- * specified string before printing.
- *
- * @param out a print writer
- * @param s a string to be printed
- *
- * @throws NullPointerException if {@code out == null}
- */
- public static void duoPrintln(PrintWriter out, String s) {
- System.out.println(s);
- out.println(s);
- }
-
- /**
- * Returns a string representation of the specified elapsed time
- * in the format "H hours M minutes S seconds".
- *
- * @param nanoseconds the elapsed time in nanoseconds
- *
- * @return a string representation of the specified elapsed time
- */
- public static String elapsedNanos(long nanoseconds) {
- long seconds = Math.round(nanoseconds /1000000000.0);
- StringBuilder sb = new StringBuilder(80);
- if (seconds >= 3600) {
- long hours = seconds / 3600;
- sb.append(hours);
- sb.append(hours==1 ? " hour " : " hours ");
- seconds %= 3600;
-
- }
- if (seconds >= 60) {
- long minutes = seconds / 60;
- sb.append(minutes);
- sb.append(minutes==1 ? " minute " : " minutes ");
- seconds %= 60;
- }
- sb.append(seconds);
- sb.append(seconds==1 ? " second" : " seconds");
- return sb.toString();
- }
-
- /**
- * Prints the specified exception, its stack trace, and
- * the specified string to standard out and then terminates the
- * Java virtual machine.
- *
- * @param s a string to be printed to standard err
- * @param e an exception or error to be printed to standard err
- *
- * @throws NullPointerException if {@code e == null}
- */
- public static void exit(String s, Throwable e) {
- e.printStackTrace(System.out);
- System.out.println(e);
- System.out.println(s);
- System.out.println("terminating program.");
- System.exit(1);
- }
-
- /**
- * Prints the specified string to standard out and then terminates the
- * Java virtual machine.
- *
- * @param s a string to be written to standard output
- */
- public static void exit(String s) {
- System.out.println(s);
- System.out.flush();
- System.exit(0);
- }
-}
-
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package blbutil;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Class {@code Utilities} contains miscellaneous static utility methods.
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class Utilities {
+
+ private Utilities() {
+ // private constructor to prevent instantiation
+ }
+
+ /**
+ * Prints a summary of memory use at the time of method invocation
+ * to standard output.
+ * @param msg a string a message to be printed with the summary
+ * of memory use
+ */
+ public static void printMemoryUse(String msg) {
+ long Mb = 1024*1024;
+ Runtime rt = Runtime.getRuntime();
+ System.out.println(Const.nl + msg
+ + Const.tab + "maxMb=" + (rt.maxMemory()/Mb)
+ + Const.tab + "totalMb=" + (rt.totalMemory()/Mb)
+ + Const.tab + "freeMb=" + (rt.freeMemory()/Mb)
+ + Const.tab + "usedMb=" + ((rt.totalMemory() - rt.freeMemory())/Mb));
+ }
+
+ /**
+ * Returns the current local time as a string. The
+ * exact details of the string representation
+ * are unspecified and subject to change.
+ *
+ * @return the current local time as a string.
+ */
+ public static String timeStamp() {
+ Date now = new Date();
+ SimpleDateFormat sdf =
+ new SimpleDateFormat("hh:mm a z 'on' dd MMM yyyy");
+ return sdf.format(now);
+ }
+
+ /**
+ * <p>Returns a set of identifiers found in a text file that has
+ * one identifier per line. The empty set is returned if
+ * {@code file == null}. Blank lines are ignored, and white-space that
+ * begins or ends a line is ignored.
+ * </p>
+ * If an {@code IOException} is thrown, an error message is printed
+ * to standard error and the Java virtual machine is forced to terminate.
+ *
+ * @param file a text file with one identifier per line
+ * @return a set of identifiers
+ *
+ * @throws IllegalArgumentException if the specified file does not exist
+ * @throws IllegalArgumentException if the specified file is a directory
+ * @throws IllegalArgumentException if any line of the specified
+ * file contains two non-white-space characters separated by one or
+ * more white-space characters
+ */
+ public static Set<String> idSet(File file) {
+ if (file==null) {
+ return Collections.emptySet();
+ }
+ else {
+ if (file.exists()==false) {
+ String s = "file does not exist: " + file;
+ throw new IllegalArgumentException(s);
+ }
+ if (file.isDirectory()) {
+ String s = "file is a directory: " + file;
+ throw new IllegalArgumentException(s);
+ }
+ Set<String> idSet = new HashSet<>();
+ try (FileIt<String> it = InputIt.fromGzipFile(file)) {
+ while (it.hasNext()) {
+ String line = it.next().trim();
+ if (line.length() > 0) {
+ if (StringUtil.countFields(line) > 1) {
+ String s = "line has >1 white-space delimited fields: "
+ + line;
+ throw new IllegalArgumentException(s);
+ }
+ idSet.add(line);
+ }
+ }
+ }
+ return idSet;
+ }
+ }
+
+ /**
+ * Prints the specified string to the specified {@code PrintWriter} and
+ * to standard out. The line separator string is not appended to the
+ * specified string before printing.
+ *
+ * @param out a print writer
+ * @param s a string to be printed
+ *
+ * @throws NullPointerException if {@code out == null}
+ */
+ public static void duoPrint(PrintWriter out, String s) {
+ System.out.print(s);
+ out.print(s);
+ }
+
+ /**
+ * Prints the specified string to the specified {@code PrintWriter} and
+ * to standard out. The line separator string is appended to the
+ * specified string before printing.
+ *
+ * @param out a print writer
+ * @param s a string to be printed
+ *
+ * @throws NullPointerException if {@code out == null}
+ */
+ public static void duoPrintln(PrintWriter out, String s) {
+ System.out.println(s);
+ out.println(s);
+ }
+
+ /**
+ * Returns a string representation of the specified elapsed time
+ * in the format "H hours M minutes S seconds".
+ *
+ * @param nanoseconds the elapsed time in nanoseconds
+ *
+ * @return a string representation of the specified elapsed time
+ */
+ public static String elapsedNanos(long nanoseconds) {
+ long seconds = Math.round(nanoseconds /1000000000.0);
+ StringBuilder sb = new StringBuilder(80);
+ if (seconds >= 3600) {
+ long hours = seconds / 3600;
+ sb.append(hours);
+ sb.append(hours==1 ? " hour " : " hours ");
+ seconds %= 3600;
+
+ }
+ if (seconds >= 60) {
+ long minutes = seconds / 60;
+ sb.append(minutes);
+ sb.append(minutes==1 ? " minute " : " minutes ");
+ seconds %= 60;
+ }
+ sb.append(seconds);
+ sb.append(seconds==1 ? " second" : " seconds");
+ return sb.toString();
+ }
+
+ /**
+ * Prints the specified exception, its stack trace, and
+ * the specified string to standard out and then terminates the
+ * Java virtual machine.
+ *
+ * @param s a string to be printed to standard err
+ * @param e an exception or error to be printed to standard err
+ *
+ * @throws NullPointerException if {@code e == null}
+ */
+ public static void exit(String s, Throwable e) {
+ e.printStackTrace(System.out);
+ System.out.println(e);
+ System.out.println(s);
+ System.out.println("terminating program.");
+ System.exit(1);
+ }
+
+ /**
+ * Prints the specified string to standard out and then terminates the
+ * Java virtual machine.
+ *
+ * @param s a string to be written to standard output
+ */
+ public static void exit(String s) {
+ System.out.println(s);
+ System.out.flush();
+ System.exit(0);
+ }
+}
+
diff --git a/dag/MergeableDagLevel.java b/dag/MergeableDagLevel.java
index 27adef9..74c8f4e 100644
--- a/dag/MergeableDagLevel.java
+++ b/dag/MergeableDagLevel.java
@@ -1,731 +1,731 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package dag;
-
-import blbutil.Const;
-import vcf.HapsMarker;
-import java.util.Arrays;
-
-/**
- * <p>Class {@code MergeableDagLevel} represents a level of a leveled
- * directed acyclic graph (DAG). The class includes a public method for
- * merging parent nodes.
- * </p>
- * <p>
- * Instances of class {@code MergebleDagLevel} are not thread-safe.
- * </p>
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class MergeableDagLevel {
-
- private MergeableDagLevel nextLevel = null;
- private MergeableDagLevel prevLevel = null;
-
- private final int levelIndex;
- private final int nAlleles;
- private final int nHaps;
- private final float[] weights;
-
- private int[][] outEdges; // [allele][parent node]
- private int[] child2FirstInEdge;
- private int[] inEdge2NextInEdge;
-
- private int[] parentNodes; // edge -> parent node
- private int[] childNodes; // edge -> child node
- private int[] symbols; // edge -> symbol
- private float[] counts; // edge -> weight
-
- private int[] child2FirstHap; // child node -> first hap index
- private int[] hap2NextHap; // current hap index -> next hap index
-
- private static float[] defaultWeights(HapsMarker data) {
- float[] fa = new float[data.nHaps()];
- Arrays.fill(fa, 1f);
- return fa;
- }
-
- /**
- * Constructs a new {@code MergeableDagLevel} instance from the specified
- * phased genotype data. The {@code previous()} method of the
- * constructed instance will return {@code null}. Each haplotype
- * will be assigned a weight of 1.
- * @param data the phased genotype data
- * @throws NullPointerException if {@code data == null}
- */
- public MergeableDagLevel(HapsMarker data) {
- this(data, defaultWeights(data));
- }
-
- /**
- * Constructs a new {@code MergeableDagLevel} instance from the specified
- * phased genotype data and haplotype weights. The {@code previous()}
- * method of the constructed instance will return {@code null}.
- * @param data the phased genotype data
- * @param weights an array mapping haplotype indices to non-negative
- * weights
- * @throws IllegalArgumentException if
- * {@code weights.length != data.nHaps()}
- * @throws NullPointerException if {@code data==null || weights==null}
- */
- public MergeableDagLevel(HapsMarker data, float[] weights) {
- checkParameters(data, weights);
- this.prevLevel = null;
- this.nextLevel = null;
- this.levelIndex = 0;
- this.nAlleles = data.marker().nAlleles();
- this.nHaps = data.nHaps();
- this.weights = weights.clone();
- allocateAndInitializeArrays(nAlleles, nHaps);
- fillArrays(data, weights);
- }
-
- /**
- * Constructs a new {@code MergeableDagLevel} instance with the
- * specified previous {@code MergeableDagLevel} and the
- * specified phased genotype data. This constructor does not alter
- * any field of the specified {@code prevLevel} object.
- * @param prevLevel the previous {@code MergeableDagLevel}
- * @param data the phased genotype data
- *
- * @throws IllegalArgumentException if
- * {@code prevLevel.nextLevel() != null}
- * @throws IllegalArgumentException if
- * {@code parent.nHaps() != data.nHaps()}
- * @throws NullPointerException if
- * {@code parent == null || data == null}
- */
- public MergeableDagLevel(MergeableDagLevel prevLevel, HapsMarker data) {
- checkParameters(prevLevel, data);
- this.prevLevel = prevLevel;
- this.nextLevel = null;
- this.levelIndex = prevLevel.index() + 1;
- this.nAlleles = data.marker().nAlleles();
- this.nHaps = data.nHaps();
- this.weights = prevLevel.weights;
- allocateAndInitializeArrays(nAlleles, nHaps);
- fillArrays(prevLevel, data, weights);
- }
-
- private void checkParameters(HapsMarker data, float[] weights) {
- if (weights.length != data.nHaps()) {
- String s = "data.nHaps()=" + data.nHaps()
- + " != weights.length=" + weights.length;
- throw new IllegalArgumentException(s);
- }
- }
-
- private void checkParameters(MergeableDagLevel parent, HapsMarker data) {
- if (parent.nextLevel!=null) {
- throw new IllegalArgumentException("parent.nextLevel!=null");
- }
- if (parent.nHaps()!=data.nHaps()) {
- throw new IllegalArgumentException("inconsistent samples");
- }
- // NB: the sequences of sample ID indices are not checked
- }
-
- private void allocateAndInitializeArrays(int nAlleles, int nHaps) {
- this.outEdges = new int[nAlleles][nHaps];
- this.child2FirstInEdge = new int[nHaps];
- this.inEdge2NextInEdge = new int[nHaps];
- this.parentNodes = new int[nHaps];
- this.childNodes = new int[nHaps];
- this.symbols = new int[nHaps];
- this.counts = new float[nHaps];
- this.child2FirstHap = new int[nHaps];
- this.hap2NextHap = new int[nHaps];
-
- for (int[] oe : outEdges) {
- Arrays.fill(oe, -1);
- }
- Arrays.fill(child2FirstInEdge, -1);
- Arrays.fill(inEdge2NextInEdge, -1);
- Arrays.fill(parentNodes, -1);
- Arrays.fill(childNodes, -1);
- Arrays.fill(symbols, -1);
- Arrays.fill(child2FirstHap, -1);
- Arrays.fill(hap2NextHap, -1);
- }
-
- private void fillArrays(HapsMarker data, float[] weights) {
- int parentNode = 0;
- for (int hap=0, n=data.nHaps(); hap<n; ++hap) {
- int symbol = data.allele(hap);
- float count = weights[hap];
- int edge = this.outEdges[symbol][parentNode];
- if (edge == -1) {
- edge = symbol;
- addEdge(parentNode, symbol, count, edge, hap);
- }
- else {
- assert edge == symbol;
- assert edge==childNodes[edge];
- int child = childNodes[edge];
- this.counts[edge] += count;
- this.hap2NextHap[hap] = this.child2FirstHap[child];
- this.child2FirstHap[child] = hap;
- }
- }
- }
-
- private void fillArrays(MergeableDagLevel prevLevel, HapsMarker data,
- float[] weights) {
- int nEdges = 0;
- for (int node=0, n=prevLevel.child2FirstHap.length; node<n; ++node) {
- if (prevLevel.child2FirstHap[node] >= 0) {
- int hap = prevLevel.child2FirstHap[node];
- while (hap != -1) {
- int symbol = data.allele(hap);
- float count = weights[hap];
- int edge = this.outEdges[symbol][node];
- if (edge == -1) {
- addEdge(node, symbol, count, nEdges++, hap);
- }
- else {
- assert edge==childNodes[edge];
- int child = childNodes[edge];
- this.counts[edge] += count;
- this.hap2NextHap[hap] = this.child2FirstHap[child];
- this.child2FirstHap[child] = hap;
- }
- hap = prevLevel.hap2NextHap[hap];
- }
- }
- }
- if (nEdges < 0.75*nHaps) {
- reduceEdgeArrayLengths(nEdges);
- }
- prevLevel.removeHaplotypeIndices();
- }
-
- private void addEdge(int parentNode, int symbol, float weight,
- int edge, int haplotype) {
- int childNode = edge;
- outEdges[symbol][parentNode] = edge;
- child2FirstInEdge[childNode] = edge;
- parentNodes[edge] = parentNode;
- childNodes[edge] = childNode;
- symbols[edge] = symbol;
- counts[edge] = weight;
- child2FirstHap[childNode] = haplotype;
- }
-
- private void reduceEdgeArrayLengths(int newLength) {
- child2FirstInEdge = Arrays.copyOf(child2FirstInEdge, newLength);
- inEdge2NextInEdge = Arrays.copyOf(inEdge2NextInEdge, newLength);
- parentNodes = Arrays.copyOf(parentNodes, newLength);
- childNodes = Arrays.copyOf(childNodes, newLength);
- symbols = Arrays.copyOf(symbols, newLength);
- counts = Arrays.copyOf(counts, newLength);
- }
-
- /**
- * Removes haplotype index data from {@code this}.
- */
- private void removeHaplotypeIndices() {
- this.child2FirstHap = null;
- this.hap2NextHap = null;
- }
-
- /**
- * Sets the previous DAG level to {@code null}, and returns
- * the previous DAG level that existed immediately prior to the invocation
- * of this method.
- * @return the previous DAG level that existed immediately prior to the
- * invocation of this method.
- */
- public MergeableDagLevel setPreviousToNull() {
- MergeableDagLevel prev = this.prevLevel;
- this.prevLevel = null;
- return prev;
- }
-
- /**
- * Sets the next level to the specified {@code MergeableDagLevel}.
- * @param nextLevel the next level
- * @throws IllegalArgumentException if
- * {@code nextLevel.previousLevel() != this}
- */
- public void setNextLevel(MergeableDagLevel nextLevel) {
- if (nextLevel.prevLevel != this) {
- throw new IllegalArgumentException("nextLevel.previousLevel!=this");
- }
- this.nextLevel = nextLevel;
- }
-
- /**
- * Returns the previous DAG level or {@code null} if no previous level
- * is stored.
- * @return the previous DAG level
- */
- public MergeableDagLevel previous() {
- return prevLevel;
- }
-
- /**
- * Returns the next DAG level or {@code null} if no next level is stored.
- * @return the next DAG level
- */
- public MergeableDagLevel next() {
- return nextLevel;
- }
-
- /**
- * Returns {@code true} if the specified parent node has a
- * sibling and returns {@code false} otherwise.
- * Two parent nodes are siblings if they are connected by an
- * edge to the same parent node at the previous level of the DAG.
- *
- * @param parentNode a parent node index
- * @return {@code true} if the specified parent node has a
- * sibling
- */
- public boolean hasSibling(int parentNode) {
- int edge = prevLevel.child2FirstInEdge[parentNode];
- while (edge>=0) {
- int pn = prevLevel.parentNodes[edge];
- int cnt = 0;
- for (int allele=0, n=prevLevel.nAlleles; allele<n; ++allele) {
- if (prevLevel.outEdges[allele][pn]>=0) {
- ++cnt;
- }
- }
- if (cnt>1) {
- return true;
- }
- edge = prevLevel.inEdge2NextInEdge[edge];
- }
- return false;
- }
-
- /**
- * Returns an immutable {@code DagLevel} corresponding to
- * {@code this}. The parent node, edge, and child node indices
- * in the returned {@code DagLevel} are the ranks of the
- * parent node, edge, and child node indices for {@code this},
- * with rank 0 corresponding to the smallest index.
- * @return an immutable {@code DagLevel} corresponding to {@code this}
- */
- public DagLevel toDagLevel() {
- float[] modCounts = DagUtil.removeValues(counts, 0f);
- int[] modSymbols = DagUtil.removeValues(symbols, -1);
- int[] modParentNodes = DagUtil.removeValues(parentNodes, -1);
- int[] modChildNodes = DagUtil.removeValues(childNodes, -1);
- if (modCounts.length<=Character.MAX_VALUE) {
- char[] mod2Symbols = toCharArray(modSymbols);
- char[] mod2ParentNodes = rankedCharValues(modParentNodes);
- char[] mod2ChildNodes = rankedCharValues(modChildNodes);
- return new LowCapacityDagLevel(mod2ParentNodes, mod2ChildNodes,
- mod2Symbols, modCounts);
- }
- else {
- int[] mod2ParentNodes = rankedIntValues(modParentNodes);
- int[] mod2ChildNodes = rankedIntValues(modChildNodes);
- return new HighCapacityDagLevel(mod2ParentNodes, mod2ChildNodes,
- modSymbols, modCounts);
- }
- }
-
- private static char[] toCharArray(int[] ia) {
- char[] ca = new char[ia.length];
- for (int j=0; j < ca.length; ++j) {
- if (ia[j] < 0 || ia[j] > Character.MAX_VALUE) {
- throw new IllegalArgumentException(String.valueOf(ia[j]));
- }
- ca[j] = (char) ia[j];
- }
- return ca;
- }
-
- /*
- * Returns an array obtained by replacing each array value with it's
- * rank when the set of array values is ordered: the smallest value
- * is replaced by 0, the next smallest value is replaced by 1, etc.
- *
- * @throws IllegalArgumentException if {@code array.length == 0}
- * @throws IllegalArgumentException if any element of the array
- * is negative
- * @throws IllegalArgumentException if the array has more than
- * {@code Character.MAX_VALUE + 1} distinct values
- * @throws NullPointerException if {@code array == null}
- */
- private static char[] rankedCharValues(int[] array) {
- if (array.length==0) {
- throw new IllegalArgumentException("array.length==0");
- }
- int[] sortedCopy = array.clone();
- Arrays.sort(sortedCopy);
- if (sortedCopy[0] < 0) {
- throw new IllegalArgumentException(String.valueOf(sortedCopy[0]));
- }
- int n = sortedCopy[sortedCopy.length - 1] + 1;
- int[] indexMap = new int[n];
- int index = 0;
- indexMap[sortedCopy[0]] = index++;
- for (int j=1; j<sortedCopy.length; ++j) {
- if (sortedCopy[j] != sortedCopy[j-1]) {
- indexMap[sortedCopy[j]] = index++;
- }
- }
- if ( (index - 1) >= Character.MAX_VALUE) {
- String s = "Array has more than (Character.MAX_VALUE + 1) values";
- throw new IllegalArgumentException(s);
- }
- char[] transformedArray = new char[array.length];
- for (int j=0; j<transformedArray.length; ++j) {
- transformedArray[j] = (char) indexMap[array[j]];
- }
- return transformedArray;
- }
-
- /*
- * Returns an array obtained by replacing each array value with it's
- * rank when the set of array values is ordered: the smallest value
- * is replaced by 0, the next smallest value is replaced by 1, etc.
- *
- * @throws IllegalArgumentException if {@code array.length == 0}
- * @throws IllegalArgumentException if any element of the array
- * is negative
- * @throws NullPointerException if {@code array == null}
- */
- private static int[] rankedIntValues(int[] array) {
- if (array.length==0) {
- throw new IllegalArgumentException("array.length==0");
- }
- int[] sortedCopy = array.clone();
- Arrays.sort(sortedCopy);
- if (sortedCopy[0] < 0) {
- throw new IllegalArgumentException(String.valueOf(sortedCopy[0]));
- }
- int n = sortedCopy[sortedCopy.length - 1] + 1;
- int[] indexMap = new int[n];
- int index = 0;
- indexMap[sortedCopy[0]] = index++;
- for (int j=1; j<sortedCopy.length; ++j) {
- if (sortedCopy[j] != sortedCopy[j-1]) {
- indexMap[sortedCopy[j]] = index++;
- }
- }
- int[] transformedArray = new int[array.length];
- for (int j=0; j<transformedArray.length; ++j) {
- transformedArray[j] = indexMap[array[j]];
- }
- return transformedArray;
- }
-
- /**
- * Merges the two specified parent nodes and assigns the specified
- * {@code retainedNode} index to the merged node.
- *
- * @param retainedNode a parent node which will receive ingoing and
- * outgoing edges of {@code removedNode}
- * @param removedNode a parent node that will be deleted after merging.
- *
- * @throws IllegalArgumentException if {@code retainedNode}
- * or {@code returnedNode} is not a valid parent node index.
- */
- public void mergeParentNodes(int retainedNode, int removedNode) {
- if (isParentNode(retainedNode)==false) {
- String s = "invalid parent node: " + retainedNode;
- throw new IllegalArgumentException(s);
- }
- if (isParentNode(removedNode)==false) {
- String s = "invalid parent node: " + removedNode;
- throw new IllegalArgumentException(s);
- }
- prevLevel.mergeChildNodes(retainedNode, removedNode);
- mergeParentNodes2(retainedNode, removedNode);
- }
-
- private void mergeParentNodes2(int retainedNode, int removedNode) {
- for (int j=0; j<nAlleles; ++j) {
- int retainedEdge = outEdges[j][retainedNode];
- int removedEdge = outEdges[j][removedNode];
- if (removedEdge >= 0) {
- if (retainedEdge == -1) {
- changeParent(removedEdge, retainedNode);
- }
- else {
- int retainedChild = childNode(retainedEdge);
- int removedChild = childNode(removedEdge);
- mergeEdges(retainedEdge, removedEdge);
- if (nextLevel != null) {
- nextLevel.mergeParentNodes2(retainedChild, removedChild);
- }
- }
- }
- }
- }
-
- /*
- * Merges the two specified child nodes and assigns the merged
- * node to the specified {@code retainedNode} index. Ingoing edges
- * to {@code removedNode} are redirected to be ingoing edges
- * to {@code retainedNode}.
- *
- * @param retainedNode a child node which will receive ingoing edges of
- * {@code removedNode}
- * @param removedNode a child node that will be deleted after merging
- */
- private void mergeChildNodes(int retainedNode, int removedNode) {
- int lastEdge = -1;
- int edge = child2FirstInEdge[removedNode];
- while (edge != -1) {
- assert childNodes[edge] == removedNode;
- childNodes[edge] = retainedNode;
- lastEdge = edge;
- edge = inEdge2NextInEdge[edge];
- }
- if (lastEdge != -1) {
- inEdge2NextInEdge[lastEdge] = child2FirstInEdge[retainedNode];
- child2FirstInEdge[retainedNode] = child2FirstInEdge[removedNode];
- child2FirstInEdge[removedNode] = -1;
- }
- }
-
- private void changeParent(int edge, int newParent) {
- int oldParent = parentNodes[edge];
- int symbol = symbols[edge];
- assert (outEdges[symbol][oldParent] == edge);
- assert (outEdges[symbol][newParent] == -1);
- outEdges[symbol][oldParent] = -1;
- outEdges[symbol][newParent] = edge;
- parentNodes[edge] = newParent;
- }
-
- private void mergeEdges(int retainedEdge, int removedEdge) {
- assert symbols[retainedEdge] == symbols[removedEdge];
- assert counts[removedEdge] > 0.0f;
- counts[retainedEdge] += counts[removedEdge];
- if (nextLevel==null) {
- mergeHaplotypes(childNodes[retainedEdge], childNodes[removedEdge]);
- }
- int parentNode = parentNodes[removedEdge];
- int childNode = childNodes[removedEdge];
- int symbol = symbols[removedEdge];
- assert inEdge2NextInEdge[child2FirstInEdge[childNode]] == -1;
- outEdges[symbol][parentNode] = -1;
- child2FirstInEdge[childNode] = -1;
- counts[removedEdge] = 0.0f;
- parentNodes[removedEdge] = -1;
- childNodes[removedEdge] = -1;
- symbols[removedEdge] = -1;
- }
-
- private void mergeHaplotypes(int retainedChild, int removedChild) {
- int hap = child2FirstHap[removedChild];
- while (hap2NextHap[hap] != -1) {
- hap = hap2NextHap[hap];
- }
- hap2NextHap[hap] = child2FirstHap[retainedChild];
- child2FirstHap[retainedChild] = child2FirstHap[removedChild];
- child2FirstHap[removedChild] = -1;
- }
-
- /**
- * Returns the marker index.
- * @return the marker index
- */
- public int index() {
- return this.levelIndex;
- }
-
- /**
- * Returns the number of sequences used to construct the DAG.
- * @return the number of sequences used to construct the DAG
- */
- public int nHaps() {
- return this.nHaps;
- }
-
- /**
- * Returns the number of alleles.
- *
- * @return the number of alleles
- */
- public int nAlleles() {
- return this.nAlleles;
- }
-
- /**
- * Returns the sum of weights for the sequences that pass
- * through the specified edge or 0 if the edge does not exist.
- *
- * @param edge index of the edge
- * @return sum of weights for the sequences that pass
- * through the specified edge or 0 if the edge does not exist
- *
- * @throws IndexOutOfBoundsException if
- * {@code edge < 0 || edge >= this.nHaps()}
- */
- public float edgeCount(int edge) {
- return counts[edge];
- }
-
- /**
- * Returns the sum of weights for the sequences that pass
- * through the specified parent node or 0 if the parent node
- * does not exist.
- *
- * @param parentNode index of the parent node
- * @return sum of weights for the sequences that pass
- * through the specified parent node or 0 if the parent node
- * does not exist
- *
- * @throws IndexOutOfBoundsException if
- * {@code parentNode < 0 || parentNode >= this.nHaps()}
- */
- public float nodeCount(int parentNode) {
- float sum = 0.0f;
- for (int symbol=0; symbol<nAlleles; ++symbol) {
- if (outEdges[symbol][parentNode] >= 0) {
- sum += edgeCount(outEdges[symbol][parentNode]);
- }
- }
- return sum;
- }
-
- /**
- * Returns an array of parent node indices.
- * @return an array of parent node indices
- */
- public int[] parentNodeArray() {
- int[] sortedReducedArray = DagUtil.removeValues(parentNodes, -1);
- Arrays.sort(sortedReducedArray);
- assert sortedReducedArray.length > 0;
- int cnt = 1;
- for (int j=1; j<sortedReducedArray.length; ++j) {
- if (sortedReducedArray[j] != sortedReducedArray[j-1]) {
- ++cnt;
- }
- }
- int[] parentNodeArray = new int[cnt];
- int index = 0;
- parentNodeArray[index++] = sortedReducedArray[0];
- for (int j=1; j<sortedReducedArray.length; ++j) {
- if (sortedReducedArray[j] != sortedReducedArray[j-1]) {
- parentNodeArray[index++] = sortedReducedArray[j];
- }
- }
- assert index==parentNodeArray.length;
- return parentNodeArray;
- }
-
- /**
- * Returns the parent node of the specified edge or -1 if the edge does
- * not exist.
- *
- * @param edge index of the edge
- * @return the parent node of the specified edge or -1 if the edge does
- * not exist
- *
- * @throws IndexOutOfBoundsException if
- * {@code edge < 0 || edge >= this.nHaps()}
- */
- public int parentNode(int edge) {
- return parentNodes[edge];
- }
-
- /**
- * Returns the child node of the specified edge or -1 if the edge does
- * not exist
- *
- * @param edge the edge
- * @return the child node of the specified edge or -1 if the edge does
- * not exist
- *
- * @throws IndexOutOfBoundsException if
- * {@code edge < 0 || edge >= this.Haplotypes()}
- */
- public int childNode(int edge) {
- return childNodes[edge];
- }
-
- /**
- * Returns the edge that is the outgoing edge of the specified
- * parent parent node having the specified symbol, or
- * returns -1 if no such edge exists.
- *
- * @param parentNode the parent node
- * @param symbol symbol labeling the outgoing edge
- * @return the edge that is the outgoing edge of the specified
- * parent parent node having the specified symbol, or
- * -1 if no such edge exists.
- *
- * @throws IndexOutOfBoundsException if
- * {@code parentNode < 0 || parentNode >= this.nHaps()}
- * @throws IndexOutOfBoundsException if
- * {@code symbol < 0 || symbol >= this.nAlleles()}
- */
- public int outEdge(int parentNode, int symbol) {
- return outEdges[symbol][parentNode];
- }
-
- /**
- * Returns a string representation of {@code this}. The exact
- * details of the representation are unspecified and subject to change.
- *
- * @return a string representation of {@code this}
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(1000);
- sb.append(Const.nl);
- sb.append("[ MergeableDagLevel: marker=");
- sb.append(levelIndex);
- sb.append(Const.nl);
- for (int j=0, n=nHaps(); j<n; ++j) {
- if (parentNodes[j] != -1) {
- sb.append("edge=");
- sb.append(j);
- sb.append(" parent=");
- sb.append(parentNodes[j]);
- sb.append(" child=");
- sb.append(childNodes[j]);
- sb.append(" symbol=");
- sb.append(symbols[j]);
- sb.append(" count=");
- sb.append(counts[j]);
- sb.append(Const.nl);
- }
- }
- sb.append("previous=");
- sb.append(prevLevel!=null);
- sb.append(" next=");
- sb.append(nextLevel!=null);
- sb.append(Const.nl);
- sb.append(" ]");
- return sb.toString();
- }
-
- private boolean isParentNode(int node) {
- if (prevLevel!=null) {
- return prevLevel.child2FirstInEdge[node]>=0;
- }
- else {
- for (int j=0; j<nAlleles; ++j) {
- if (outEdges[j][node] != -1) {
- return true;
- }
- }
- return false;
- }
- }
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package dag;
+
+import blbutil.Const;
+import vcf.HapsMarker;
+import java.util.Arrays;
+
+/**
+ * <p>Class {@code MergeableDagLevel} represents a level of a leveled
+ * directed acyclic graph (DAG). The class includes a public method for
+ * merging parent nodes.
+ * </p>
+ * <p>
+ * Instances of class {@code MergebleDagLevel} are not thread-safe.
+ * </p>
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class MergeableDagLevel {
+
+ private MergeableDagLevel nextLevel = null;
+ private MergeableDagLevel prevLevel = null;
+
+ private final int levelIndex;
+ private final int nAlleles;
+ private final int nHaps;
+ private final float[] weights;
+
+ private int[][] outEdges; // [allele][parent node]
+ private int[] child2FirstInEdge;
+ private int[] inEdge2NextInEdge;
+
+ private int[] parentNodes; // edge -> parent node
+ private int[] childNodes; // edge -> child node
+ private int[] symbols; // edge -> symbol
+ private float[] counts; // edge -> weight
+
+ private int[] child2FirstHap; // child node -> first hap index
+ private int[] hap2NextHap; // current hap index -> next hap index
+
+ private static float[] defaultWeights(HapsMarker data) {
+ float[] fa = new float[data.nHaps()];
+ Arrays.fill(fa, 1f);
+ return fa;
+ }
+
+ /**
+ * Constructs a new {@code MergeableDagLevel} instance from the specified
+ * phased genotype data. The {@code previous()} method of the
+ * constructed instance will return {@code null}. Each haplotype
+ * will be assigned a weight of 1.
+ * @param data the phased genotype data
+ * @throws NullPointerException if {@code data == null}
+ */
+ public MergeableDagLevel(HapsMarker data) {
+ this(data, defaultWeights(data));
+ }
+
+ /**
+ * Constructs a new {@code MergeableDagLevel} instance from the specified
+ * phased genotype data and haplotype weights. The {@code previous()}
+ * method of the constructed instance will return {@code null}.
+ * @param data the phased genotype data
+ * @param weights an array mapping haplotype indices to non-negative
+ * weights
+ * @throws IllegalArgumentException if
+ * {@code weights.length != data.nHaps()}
+ * @throws NullPointerException if {@code data==null || weights==null}
+ */
+ public MergeableDagLevel(HapsMarker data, float[] weights) {
+ checkParameters(data, weights);
+ this.prevLevel = null;
+ this.nextLevel = null;
+ this.levelIndex = 0;
+ this.nAlleles = data.marker().nAlleles();
+ this.nHaps = data.nHaps();
+ this.weights = weights.clone();
+ allocateAndInitializeArrays(nAlleles, nHaps);
+ fillArrays(data, weights);
+ }
+
+ /**
+ * Constructs a new {@code MergeableDagLevel} instance with the
+ * specified previous {@code MergeableDagLevel} and the
+ * specified phased genotype data. This constructor does not alter
+ * any field of the specified {@code prevLevel} object.
+ * @param prevLevel the previous {@code MergeableDagLevel}
+ * @param data the phased genotype data
+ *
+ * @throws IllegalArgumentException if
+ * {@code prevLevel.nextLevel() != null}
+ * @throws IllegalArgumentException if
+ * {@code parent.nHaps() != data.nHaps()}
+ * @throws NullPointerException if
+ * {@code parent == null || data == null}
+ */
+ public MergeableDagLevel(MergeableDagLevel prevLevel, HapsMarker data) {
+ checkParameters(prevLevel, data);
+ this.prevLevel = prevLevel;
+ this.nextLevel = null;
+ this.levelIndex = prevLevel.index() + 1;
+ this.nAlleles = data.marker().nAlleles();
+ this.nHaps = data.nHaps();
+ this.weights = prevLevel.weights;
+ allocateAndInitializeArrays(nAlleles, nHaps);
+ fillArrays(prevLevel, data, weights);
+ }
+
+ private void checkParameters(HapsMarker data, float[] weights) {
+ if (weights.length != data.nHaps()) {
+ String s = "data.nHaps()=" + data.nHaps()
+ + " != weights.length=" + weights.length;
+ throw new IllegalArgumentException(s);
+ }
+ }
+
+ private void checkParameters(MergeableDagLevel parent, HapsMarker data) {
+ if (parent.nextLevel!=null) {
+ throw new IllegalArgumentException("parent.nextLevel!=null");
+ }
+ if (parent.nHaps()!=data.nHaps()) {
+ throw new IllegalArgumentException("inconsistent samples");
+ }
+ // NB: the sequences of sample ID indices are not checked
+ }
+
+ private void allocateAndInitializeArrays(int nAlleles, int nHaps) {
+ this.outEdges = new int[nAlleles][nHaps];
+ this.child2FirstInEdge = new int[nHaps];
+ this.inEdge2NextInEdge = new int[nHaps];
+ this.parentNodes = new int[nHaps];
+ this.childNodes = new int[nHaps];
+ this.symbols = new int[nHaps];
+ this.counts = new float[nHaps];
+ this.child2FirstHap = new int[nHaps];
+ this.hap2NextHap = new int[nHaps];
+
+ for (int[] oe : outEdges) {
+ Arrays.fill(oe, -1);
+ }
+ Arrays.fill(child2FirstInEdge, -1);
+ Arrays.fill(inEdge2NextInEdge, -1);
+ Arrays.fill(parentNodes, -1);
+ Arrays.fill(childNodes, -1);
+ Arrays.fill(symbols, -1);
+ Arrays.fill(child2FirstHap, -1);
+ Arrays.fill(hap2NextHap, -1);
+ }
+
+ private void fillArrays(HapsMarker data, float[] weights) {
+ int parentNode = 0;
+ for (int hap=0, n=data.nHaps(); hap<n; ++hap) {
+ int symbol = data.allele(hap);
+ float count = weights[hap];
+ int edge = this.outEdges[symbol][parentNode];
+ if (edge == -1) {
+ edge = symbol;
+ addEdge(parentNode, symbol, count, edge, hap);
+ }
+ else {
+ assert edge == symbol;
+ assert edge==childNodes[edge];
+ int child = childNodes[edge];
+ this.counts[edge] += count;
+ this.hap2NextHap[hap] = this.child2FirstHap[child];
+ this.child2FirstHap[child] = hap;
+ }
+ }
+ }
+
+ private void fillArrays(MergeableDagLevel prevLevel, HapsMarker data,
+ float[] weights) {
+ int nEdges = 0;
+ for (int node=0, n=prevLevel.child2FirstHap.length; node<n; ++node) {
+ if (prevLevel.child2FirstHap[node] >= 0) {
+ int hap = prevLevel.child2FirstHap[node];
+ while (hap != -1) {
+ int symbol = data.allele(hap);
+ float count = weights[hap];
+ int edge = this.outEdges[symbol][node];
+ if (edge == -1) {
+ addEdge(node, symbol, count, nEdges++, hap);
+ }
+ else {
+ assert edge==childNodes[edge];
+ int child = childNodes[edge];
+ this.counts[edge] += count;
+ this.hap2NextHap[hap] = this.child2FirstHap[child];
+ this.child2FirstHap[child] = hap;
+ }
+ hap = prevLevel.hap2NextHap[hap];
+ }
+ }
+ }
+ if (nEdges < 0.75*nHaps) {
+ reduceEdgeArrayLengths(nEdges);
+ }
+ prevLevel.removeHaplotypeIndices();
+ }
+
+ private void addEdge(int parentNode, int symbol, float weight,
+ int edge, int haplotype) {
+ int childNode = edge;
+ outEdges[symbol][parentNode] = edge;
+ child2FirstInEdge[childNode] = edge;
+ parentNodes[edge] = parentNode;
+ childNodes[edge] = childNode;
+ symbols[edge] = symbol;
+ counts[edge] = weight;
+ child2FirstHap[childNode] = haplotype;
+ }
+
+ private void reduceEdgeArrayLengths(int newLength) {
+ child2FirstInEdge = Arrays.copyOf(child2FirstInEdge, newLength);
+ inEdge2NextInEdge = Arrays.copyOf(inEdge2NextInEdge, newLength);
+ parentNodes = Arrays.copyOf(parentNodes, newLength);
+ childNodes = Arrays.copyOf(childNodes, newLength);
+ symbols = Arrays.copyOf(symbols, newLength);
+ counts = Arrays.copyOf(counts, newLength);
+ }
+
+ /**
+ * Removes haplotype index data from {@code this}.
+ */
+ private void removeHaplotypeIndices() {
+ this.child2FirstHap = null;
+ this.hap2NextHap = null;
+ }
+
+ /**
+ * Sets the previous DAG level to {@code null}, and returns
+ * the previous DAG level that existed immediately prior to the invocation
+ * of this method.
+ * @return the previous DAG level that existed immediately prior to the
+ * invocation of this method.
+ */
+ public MergeableDagLevel setPreviousToNull() {
+ MergeableDagLevel prev = this.prevLevel;
+ this.prevLevel = null;
+ return prev;
+ }
+
+ /**
+ * Sets the next level to the specified {@code MergeableDagLevel}.
+ * @param nextLevel the next level
+ * @throws IllegalArgumentException if
+ * {@code nextLevel.previousLevel() != this}
+ */
+ public void setNextLevel(MergeableDagLevel nextLevel) {
+ if (nextLevel.prevLevel != this) {
+ throw new IllegalArgumentException("nextLevel.previousLevel!=this");
+ }
+ this.nextLevel = nextLevel;
+ }
+
+ /**
+ * Returns the previous DAG level or {@code null} if no previous level
+ * is stored.
+ * @return the previous DAG level
+ */
+ public MergeableDagLevel previous() {
+ return prevLevel;
+ }
+
+ /**
+ * Returns the next DAG level or {@code null} if no next level is stored.
+ * @return the next DAG level
+ */
+ public MergeableDagLevel next() {
+ return nextLevel;
+ }
+
+ /**
+ * Returns {@code true} if the specified parent node has a
+ * sibling and returns {@code false} otherwise.
+ * Two parent nodes are siblings if they are connected by an
+ * edge to the same parent node at the previous level of the DAG.
+ *
+ * @param parentNode a parent node index
+ * @return {@code true} if the specified parent node has a
+ * sibling
+ */
+ public boolean hasSibling(int parentNode) {
+ int edge = prevLevel.child2FirstInEdge[parentNode];
+ while (edge>=0) {
+ int pn = prevLevel.parentNodes[edge];
+ int cnt = 0;
+ for (int allele=0, n=prevLevel.nAlleles; allele<n; ++allele) {
+ if (prevLevel.outEdges[allele][pn]>=0) {
+ ++cnt;
+ }
+ }
+ if (cnt>1) {
+ return true;
+ }
+ edge = prevLevel.inEdge2NextInEdge[edge];
+ }
+ return false;
+ }
+
+ /**
+ * Returns an immutable {@code DagLevel} corresponding to
+ * {@code this}. The parent node, edge, and child node indices
+ * in the returned {@code DagLevel} are the ranks of the
+ * parent node, edge, and child node indices for {@code this},
+ * with rank 0 corresponding to the smallest index.
+ * @return an immutable {@code DagLevel} corresponding to {@code this}
+ */
+ public DagLevel toDagLevel() {
+ float[] modCounts = DagUtil.removeValues(counts, 0f);
+ int[] modSymbols = DagUtil.removeValues(symbols, -1);
+ int[] modParentNodes = DagUtil.removeValues(parentNodes, -1);
+ int[] modChildNodes = DagUtil.removeValues(childNodes, -1);
+ if (modCounts.length<=Character.MAX_VALUE) {
+ char[] mod2Symbols = toCharArray(modSymbols);
+ char[] mod2ParentNodes = rankedCharValues(modParentNodes);
+ char[] mod2ChildNodes = rankedCharValues(modChildNodes);
+ return new LowCapacityDagLevel(mod2ParentNodes, mod2ChildNodes,
+ mod2Symbols, modCounts);
+ }
+ else {
+ int[] mod2ParentNodes = rankedIntValues(modParentNodes);
+ int[] mod2ChildNodes = rankedIntValues(modChildNodes);
+ return new HighCapacityDagLevel(mod2ParentNodes, mod2ChildNodes,
+ modSymbols, modCounts);
+ }
+ }
+
+ private static char[] toCharArray(int[] ia) {
+ char[] ca = new char[ia.length];
+ for (int j=0; j < ca.length; ++j) {
+ if (ia[j] < 0 || ia[j] > Character.MAX_VALUE) {
+ throw new IllegalArgumentException(String.valueOf(ia[j]));
+ }
+ ca[j] = (char) ia[j];
+ }
+ return ca;
+ }
+
+ /*
+ * Returns an array obtained by replacing each array value with it's
+ * rank when the set of array values is ordered: the smallest value
+ * is replaced by 0, the next smallest value is replaced by 1, etc.
+ *
+ * @throws IllegalArgumentException if {@code array.length == 0}
+ * @throws IllegalArgumentException if any element of the array
+ * is negative
+ * @throws IllegalArgumentException if the array has more than
+ * {@code Character.MAX_VALUE + 1} distinct values
+ * @throws NullPointerException if {@code array == null}
+ */
+ private static char[] rankedCharValues(int[] array) {
+ if (array.length==0) {
+ throw new IllegalArgumentException("array.length==0");
+ }
+ int[] sortedCopy = array.clone();
+ Arrays.sort(sortedCopy);
+ if (sortedCopy[0] < 0) {
+ throw new IllegalArgumentException(String.valueOf(sortedCopy[0]));
+ }
+ int n = sortedCopy[sortedCopy.length - 1] + 1;
+ int[] indexMap = new int[n];
+ int index = 0;
+ indexMap[sortedCopy[0]] = index++;
+ for (int j=1; j<sortedCopy.length; ++j) {
+ if (sortedCopy[j] != sortedCopy[j-1]) {
+ indexMap[sortedCopy[j]] = index++;
+ }
+ }
+ if ( (index - 1) >= Character.MAX_VALUE) {
+ String s = "Array has more than (Character.MAX_VALUE + 1) values";
+ throw new IllegalArgumentException(s);
+ }
+ char[] transformedArray = new char[array.length];
+ for (int j=0; j<transformedArray.length; ++j) {
+ transformedArray[j] = (char) indexMap[array[j]];
+ }
+ return transformedArray;
+ }
+
+ /*
+ * Returns an array obtained by replacing each array value with it's
+ * rank when the set of array values is ordered: the smallest value
+ * is replaced by 0, the next smallest value is replaced by 1, etc.
+ *
+ * @throws IllegalArgumentException if {@code array.length == 0}
+ * @throws IllegalArgumentException if any element of the array
+ * is negative
+ * @throws NullPointerException if {@code array == null}
+ */
+ private static int[] rankedIntValues(int[] array) {
+ if (array.length==0) {
+ throw new IllegalArgumentException("array.length==0");
+ }
+ int[] sortedCopy = array.clone();
+ Arrays.sort(sortedCopy);
+ if (sortedCopy[0] < 0) {
+ throw new IllegalArgumentException(String.valueOf(sortedCopy[0]));
+ }
+ int n = sortedCopy[sortedCopy.length - 1] + 1;
+ int[] indexMap = new int[n];
+ int index = 0;
+ indexMap[sortedCopy[0]] = index++;
+ for (int j=1; j<sortedCopy.length; ++j) {
+ if (sortedCopy[j] != sortedCopy[j-1]) {
+ indexMap[sortedCopy[j]] = index++;
+ }
+ }
+ int[] transformedArray = new int[array.length];
+ for (int j=0; j<transformedArray.length; ++j) {
+ transformedArray[j] = indexMap[array[j]];
+ }
+ return transformedArray;
+ }
+
+ /**
+ * Merges the two specified parent nodes and assigns the specified
+ * {@code retainedNode} index to the merged node.
+ *
+ * @param retainedNode a parent node which will receive ingoing and
+ * outgoing edges of {@code removedNode}
+ * @param removedNode a parent node that will be deleted after merging.
+ *
+ * @throws IllegalArgumentException if {@code retainedNode}
+ * or {@code returnedNode} is not a valid parent node index.
+ */
+ public void mergeParentNodes(int retainedNode, int removedNode) {
+ if (isParentNode(retainedNode)==false) {
+ String s = "invalid parent node: " + retainedNode;
+ throw new IllegalArgumentException(s);
+ }
+ if (isParentNode(removedNode)==false) {
+ String s = "invalid parent node: " + removedNode;
+ throw new IllegalArgumentException(s);
+ }
+ prevLevel.mergeChildNodes(retainedNode, removedNode);
+ mergeParentNodes2(retainedNode, removedNode);
+ }
+
+ private void mergeParentNodes2(int retainedNode, int removedNode) {
+ for (int j=0; j<nAlleles; ++j) {
+ int retainedEdge = outEdges[j][retainedNode];
+ int removedEdge = outEdges[j][removedNode];
+ if (removedEdge >= 0) {
+ if (retainedEdge == -1) {
+ changeParent(removedEdge, retainedNode);
+ }
+ else {
+ int retainedChild = childNode(retainedEdge);
+ int removedChild = childNode(removedEdge);
+ mergeEdges(retainedEdge, removedEdge);
+ if (nextLevel != null) {
+ nextLevel.mergeParentNodes2(retainedChild, removedChild);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Merges the two specified child nodes and assigns the merged
+ * node to the specified {@code retainedNode} index. Ingoing edges
+ * to {@code removedNode} are redirected to be ingoing edges
+ * to {@code retainedNode}.
+ *
+ * @param retainedNode a child node which will receive ingoing edges of
+ * {@code removedNode}
+ * @param removedNode a child node that will be deleted after merging
+ */
+ private void mergeChildNodes(int retainedNode, int removedNode) {
+ int lastEdge = -1;
+ int edge = child2FirstInEdge[removedNode];
+ while (edge != -1) {
+ assert childNodes[edge] == removedNode;
+ childNodes[edge] = retainedNode;
+ lastEdge = edge;
+ edge = inEdge2NextInEdge[edge];
+ }
+ if (lastEdge != -1) {
+ inEdge2NextInEdge[lastEdge] = child2FirstInEdge[retainedNode];
+ child2FirstInEdge[retainedNode] = child2FirstInEdge[removedNode];
+ child2FirstInEdge[removedNode] = -1;
+ }
+ }
+
+ private void changeParent(int edge, int newParent) {
+ int oldParent = parentNodes[edge];
+ int symbol = symbols[edge];
+ assert (outEdges[symbol][oldParent] == edge);
+ assert (outEdges[symbol][newParent] == -1);
+ outEdges[symbol][oldParent] = -1;
+ outEdges[symbol][newParent] = edge;
+ parentNodes[edge] = newParent;
+ }
+
+ private void mergeEdges(int retainedEdge, int removedEdge) {
+ assert symbols[retainedEdge] == symbols[removedEdge];
+ assert counts[removedEdge] > 0.0f;
+ counts[retainedEdge] += counts[removedEdge];
+ if (nextLevel==null) {
+ mergeHaplotypes(childNodes[retainedEdge], childNodes[removedEdge]);
+ }
+ int parentNode = parentNodes[removedEdge];
+ int childNode = childNodes[removedEdge];
+ int symbol = symbols[removedEdge];
+ assert inEdge2NextInEdge[child2FirstInEdge[childNode]] == -1;
+ outEdges[symbol][parentNode] = -1;
+ child2FirstInEdge[childNode] = -1;
+ counts[removedEdge] = 0.0f;
+ parentNodes[removedEdge] = -1;
+ childNodes[removedEdge] = -1;
+ symbols[removedEdge] = -1;
+ }
+
+ private void mergeHaplotypes(int retainedChild, int removedChild) {
+ int hap = child2FirstHap[removedChild];
+ while (hap2NextHap[hap] != -1) {
+ hap = hap2NextHap[hap];
+ }
+ hap2NextHap[hap] = child2FirstHap[retainedChild];
+ child2FirstHap[retainedChild] = child2FirstHap[removedChild];
+ child2FirstHap[removedChild] = -1;
+ }
+
+ /**
+ * Returns the marker index.
+ * @return the marker index
+ */
+ public int index() {
+ return this.levelIndex;
+ }
+
+ /**
+ * Returns the number of sequences used to construct the DAG.
+ * @return the number of sequences used to construct the DAG
+ */
+ public int nHaps() {
+ return this.nHaps;
+ }
+
+ /**
+ * Returns the number of alleles.
+ *
+ * @return the number of alleles
+ */
+ public int nAlleles() {
+ return this.nAlleles;
+ }
+
+ /**
+ * Returns the sum of weights for the sequences that pass
+ * through the specified edge or 0 if the edge does not exist.
+ *
+ * @param edge index of the edge
+ * @return sum of weights for the sequences that pass
+ * through the specified edge or 0 if the edge does not exist
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code edge < 0 || edge >= this.nHaps()}
+ */
+ public float edgeCount(int edge) {
+ return counts[edge];
+ }
+
+ /**
+ * Returns the sum of weights for the sequences that pass
+ * through the specified parent node or 0 if the parent node
+ * does not exist.
+ *
+ * @param parentNode index of the parent node
+ * @return sum of weights for the sequences that pass
+ * through the specified parent node or 0 if the parent node
+ * does not exist
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code parentNode < 0 || parentNode >= this.nHaps()}
+ */
+ public float nodeCount(int parentNode) {
+ float sum = 0.0f;
+ for (int symbol=0; symbol<nAlleles; ++symbol) {
+ if (outEdges[symbol][parentNode] >= 0) {
+ sum += edgeCount(outEdges[symbol][parentNode]);
+ }
+ }
+ return sum;
+ }
+
+ /**
+ * Returns an array of parent node indices.
+ * @return an array of parent node indices
+ */
+ public int[] parentNodeArray() {
+ int[] sortedReducedArray = DagUtil.removeValues(parentNodes, -1);
+ Arrays.sort(sortedReducedArray);
+ assert sortedReducedArray.length > 0;
+ int cnt = 1;
+ for (int j=1; j<sortedReducedArray.length; ++j) {
+ if (sortedReducedArray[j] != sortedReducedArray[j-1]) {
+ ++cnt;
+ }
+ }
+ int[] parentNodeArray = new int[cnt];
+ int index = 0;
+ parentNodeArray[index++] = sortedReducedArray[0];
+ for (int j=1; j<sortedReducedArray.length; ++j) {
+ if (sortedReducedArray[j] != sortedReducedArray[j-1]) {
+ parentNodeArray[index++] = sortedReducedArray[j];
+ }
+ }
+ assert index==parentNodeArray.length;
+ return parentNodeArray;
+ }
+
+ /**
+ * Returns the parent node of the specified edge or -1 if the edge does
+ * not exist.
+ *
+ * @param edge index of the edge
+ * @return the parent node of the specified edge or -1 if the edge does
+ * not exist
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code edge < 0 || edge >= this.nHaps()}
+ */
+ public int parentNode(int edge) {
+ return parentNodes[edge];
+ }
+
+ /**
+ * Returns the child node of the specified edge or -1 if the edge does
+ * not exist
+ *
+ * @param edge the edge
+ * @return the child node of the specified edge or -1 if the edge does
+ * not exist
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code edge < 0 || edge >= this.Haplotypes()}
+ */
+ public int childNode(int edge) {
+ return childNodes[edge];
+ }
+
+ /**
+ * Returns the edge that is the outgoing edge of the specified
+ * parent parent node having the specified symbol, or
+ * returns -1 if no such edge exists.
+ *
+ * @param parentNode the parent node
+ * @param symbol symbol labeling the outgoing edge
+ * @return the edge that is the outgoing edge of the specified
+ * parent parent node having the specified symbol, or
+ * -1 if no such edge exists.
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code parentNode < 0 || parentNode >= this.nHaps()}
+ * @throws IndexOutOfBoundsException if
+ * {@code symbol < 0 || symbol >= this.nAlleles()}
+ */
+ public int outEdge(int parentNode, int symbol) {
+ return outEdges[symbol][parentNode];
+ }
+
+ /**
+ * Returns a string representation of {@code this}. The exact
+ * details of the representation are unspecified and subject to change.
+ *
+ * @return a string representation of {@code this}
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(1000);
+ sb.append(Const.nl);
+ sb.append("[ MergeableDagLevel: marker=");
+ sb.append(levelIndex);
+ sb.append(Const.nl);
+ for (int j=0, n=nHaps(); j<n; ++j) {
+ if (parentNodes[j] != -1) {
+ sb.append("edge=");
+ sb.append(j);
+ sb.append(" parent=");
+ sb.append(parentNodes[j]);
+ sb.append(" child=");
+ sb.append(childNodes[j]);
+ sb.append(" symbol=");
+ sb.append(symbols[j]);
+ sb.append(" count=");
+ sb.append(counts[j]);
+ sb.append(Const.nl);
+ }
+ }
+ sb.append("previous=");
+ sb.append(prevLevel!=null);
+ sb.append(" next=");
+ sb.append(nextLevel!=null);
+ sb.append(Const.nl);
+ sb.append(" ]");
+ return sb.toString();
+ }
+
+ private boolean isParentNode(int node) {
+ if (prevLevel!=null) {
+ return prevLevel.child2FirstInEdge[node]>=0;
+ }
+ else {
+ for (int j=0; j<nAlleles; ++j) {
+ if (outEdges[j][node] != -1) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
}
\ No newline at end of file
diff --git a/gpl_license b/gpl_license
index 94a9ed0..818433e 100644
--- a/gpl_license
+++ b/gpl_license
@@ -1,674 +1,674 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program 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.
-
- This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- <program> Copyright (C) <year> <name of author>
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<http://www.gnu.org/licenses/>.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program 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.
+
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/haplotype/RefHapPairs.java b/haplotype/RefHapPairs.java
index 14f0dc7..5014db4 100644
--- a/haplotype/RefHapPairs.java
+++ b/haplotype/RefHapPairs.java
@@ -1,189 +1,189 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package haplotype;
-
-import beagleutil.Samples;
-import vcf.Marker;
-import vcf.Markers;
-import vcf.VcfEmission;
-
-/**
- * <p>Class {@code RefHapPairs} stores a list of samples and a
- * haplotype pair for each sample.
- * </p>
- * <p>Instances of class {@code RefHapPairs} are immutable.<p>
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class RefHapPairs implements SampleHapPairs {
-
- private final Markers markers;
- private final Samples samples;
- private final VcfEmission[] refVcfRecs;
-
- /**
- * Constructs a new {@code RefHapPairs} instance.
- * @param markers the sequence of markers
- * @param samples the sequence of samples
- * @param refVcfRecs the sequence of per-marker genotype data
- *
- * @throws IllegalArgumentException if
- * {@code markers.nMarkers() != refVcfRecs.length}
- * @throws IllegalArgumentException if
- * {@code refVcfRecs[k].samples().equals(samples) == false} for any
- * {@code k} satisfying {@code 0 <= k && k < refVcfRecs.length}
- * @throws IllegalArgumentException if
- * {@code refVcfRecs[k].marker().equals(markers.marker(k)) == false}
- * for any {@code k} satisfying {@code 0 <= k && k < refVcfRecs.length}
- * @throws IllegalArgumentException if
- * {@code refVcfRecs[k].isRefData() == false} for any {@code k}
- * satisfying {@code 0 <= k && k < refVcfRecs.length}
- * @throws NullPointerException if
- * {@code markers == null || samples == null || refVcfRecs == null
- * || refVcfRecs[k] == null} for any {@code k} satisfying
- * {@code 0 <= k && k <= refVcfRecs.length}
- */
- public RefHapPairs(Markers markers, Samples samples,
- VcfEmission[] refVcfRecs) {
- checkPhasedMarkers(markers, samples, refVcfRecs);
- this.markers = markers;
- this.samples = samples;
- this.refVcfRecs = refVcfRecs.clone();
- }
-
- private static void checkPhasedMarkers(Markers markers, Samples samples,
- VcfEmission[] refVcfRecs) {
- if (markers.nMarkers()!=refVcfRecs.length) {
- String s = "markers.nMarkers()=" + markers.nMarkers()
- + " refVcfRecs.length=" + refVcfRecs.length;
- throw new IllegalArgumentException(s);
- }
- for (int j=0; j<refVcfRecs.length; ++j) {
- if (refVcfRecs[j].samples().equals(samples)==false) {
- String s = "sample inconsistency at index " + j;
- throw new IllegalArgumentException(s);
- }
- if (refVcfRecs[j].marker().equals(markers.marker(j))==false) {
- String s = "marker inconsistency at index " + j;
- throw new IllegalArgumentException(s);
- }
- if (refVcfRecs[j].isRefData()==false) {
- String s = "non-reference data at marker index " + j;
- throw new IllegalArgumentException(s);
- }
- }
- }
-
- @Override
- public int allele1(int marker, int hapPair) {
- return refVcfRecs[marker].allele1(hapPair);
- }
-
- @Override
- public int allele2(int marker, int hapPair) {
- return refVcfRecs[marker].allele2(hapPair);
- }
-
- @Override
- public int allele(int marker, int haplotype) {
- int hapPair = haplotype/2;
- if ((haplotype & 1)==0) {
- return refVcfRecs[marker].allele1(hapPair);
- }
- else {
- return refVcfRecs[marker].allele2(hapPair);
- }
- }
-
- @Override
- public int nMarkers() {
- return markers.nMarkers();
- }
-
- @Override
- public Markers markers() {
- return markers;
- }
-
- @Override
- public Marker marker(int marker) {
- return markers.marker(marker);
- }
-
- @Override
- public int nHaps() {
- return 2*samples.nSamples();
- }
-
- @Override
- public int nHapPairs() {
- return samples.nSamples();
- }
-
- @Override
- public int nSamples() {
- return samples.nSamples();
- }
-
- @Override
- public Samples samples() {
- return samples;
- }
-
- @Override
- public Samples samples(int hapPair) {
- if (hapPair < 0 || hapPair >= samples.nSamples()) {
- throw new IndexOutOfBoundsException(String.valueOf(hapPair));
- }
- return samples;
- }
-
- @Override
- public int sampleIndex(int hapPair) {
- if (hapPair < 0 || hapPair >= samples.nSamples()) {
- throw new IndexOutOfBoundsException(String.valueOf(hapPair));
- }
- return hapPair;
- }
-
- @Override
- public int nAlleles(int marker) {
- return refVcfRecs[marker].nAlleles();
- }
-
- @Override
- public boolean storesNonMajorIndices(int marker) {
- return refVcfRecs[marker].storesNonMajorIndices();
- }
-
- @Override
- public int majorAllele(int marker) {
- return refVcfRecs[marker].majorAllele();
- }
-
- @Override
- public int alleleCount(int marker, int allele) {
- return refVcfRecs[marker].alleleCount(allele);
- }
-
- @Override
- public int hapIndex(int marker, int allele, int copy) {
- return refVcfRecs[marker].hapIndex(allele, copy);
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package haplotype;
+
+import beagleutil.Samples;
+import vcf.Marker;
+import vcf.Markers;
+import vcf.VcfEmission;
+
+/**
+ * <p>Class {@code RefHapPairs} stores a list of samples and a
+ * haplotype pair for each sample.
+ * </p>
+ * <p>Instances of class {@code RefHapPairs} are immutable.<p>
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class RefHapPairs implements SampleHapPairs {
+
+ private final Markers markers;
+ private final Samples samples;
+ private final VcfEmission[] refVcfRecs;
+
+ /**
+ * Constructs a new {@code RefHapPairs} instance.
+ * @param markers the sequence of markers
+ * @param samples the sequence of samples
+ * @param refVcfRecs the sequence of per-marker genotype data
+ *
+ * @throws IllegalArgumentException if
+ * {@code markers.nMarkers() != refVcfRecs.length}
+ * @throws IllegalArgumentException if
+ * {@code refVcfRecs[k].samples().equals(samples) == false} for any
+ * {@code k} satisfying {@code 0 <= k && k < refVcfRecs.length}
+ * @throws IllegalArgumentException if
+ * {@code refVcfRecs[k].marker().equals(markers.marker(k)) == false}
+ * for any {@code k} satisfying {@code 0 <= k && k < refVcfRecs.length}
+ * @throws IllegalArgumentException if
+ * {@code refVcfRecs[k].isRefData() == false} for any {@code k}
+ * satisfying {@code 0 <= k && k < refVcfRecs.length}
+ * @throws NullPointerException if
+ * {@code markers == null || samples == null || refVcfRecs == null
+ * || refVcfRecs[k] == null} for any {@code k} satisfying
+ * {@code 0 <= k && k <= refVcfRecs.length}
+ */
+ public RefHapPairs(Markers markers, Samples samples,
+ VcfEmission[] refVcfRecs) {
+ checkPhasedMarkers(markers, samples, refVcfRecs);
+ this.markers = markers;
+ this.samples = samples;
+ this.refVcfRecs = refVcfRecs.clone();
+ }
+
+ private static void checkPhasedMarkers(Markers markers, Samples samples,
+ VcfEmission[] refVcfRecs) {
+ if (markers.nMarkers()!=refVcfRecs.length) {
+ String s = "markers.nMarkers()=" + markers.nMarkers()
+ + " refVcfRecs.length=" + refVcfRecs.length;
+ throw new IllegalArgumentException(s);
+ }
+ for (int j=0; j<refVcfRecs.length; ++j) {
+ if (refVcfRecs[j].samples().equals(samples)==false) {
+ String s = "sample inconsistency at index " + j;
+ throw new IllegalArgumentException(s);
+ }
+ if (refVcfRecs[j].marker().equals(markers.marker(j))==false) {
+ String s = "marker inconsistency at index " + j;
+ throw new IllegalArgumentException(s);
+ }
+ if (refVcfRecs[j].isRefData()==false) {
+ String s = "non-reference data at marker index " + j;
+ throw new IllegalArgumentException(s);
+ }
+ }
+ }
+
+ @Override
+ public int allele1(int marker, int hapPair) {
+ return refVcfRecs[marker].allele1(hapPair);
+ }
+
+ @Override
+ public int allele2(int marker, int hapPair) {
+ return refVcfRecs[marker].allele2(hapPair);
+ }
+
+ @Override
+ public int allele(int marker, int haplotype) {
+ int hapPair = haplotype/2;
+ if ((haplotype & 1)==0) {
+ return refVcfRecs[marker].allele1(hapPair);
+ }
+ else {
+ return refVcfRecs[marker].allele2(hapPair);
+ }
+ }
+
+ @Override
+ public int nMarkers() {
+ return markers.nMarkers();
+ }
+
+ @Override
+ public Markers markers() {
+ return markers;
+ }
+
+ @Override
+ public Marker marker(int marker) {
+ return markers.marker(marker);
+ }
+
+ @Override
+ public int nHaps() {
+ return 2*samples.nSamples();
+ }
+
+ @Override
+ public int nHapPairs() {
+ return samples.nSamples();
+ }
+
+ @Override
+ public int nSamples() {
+ return samples.nSamples();
+ }
+
+ @Override
+ public Samples samples() {
+ return samples;
+ }
+
+ @Override
+ public Samples samples(int hapPair) {
+ if (hapPair < 0 || hapPair >= samples.nSamples()) {
+ throw new IndexOutOfBoundsException(String.valueOf(hapPair));
+ }
+ return samples;
+ }
+
+ @Override
+ public int sampleIndex(int hapPair) {
+ if (hapPair < 0 || hapPair >= samples.nSamples()) {
+ throw new IndexOutOfBoundsException(String.valueOf(hapPair));
+ }
+ return hapPair;
+ }
+
+ @Override
+ public int nAlleles(int marker) {
+ return refVcfRecs[marker].nAlleles();
+ }
+
+ @Override
+ public boolean storesNonMajorIndices(int marker) {
+ return refVcfRecs[marker].storesNonMajorIndices();
+ }
+
+ @Override
+ public int majorAllele(int marker) {
+ return refVcfRecs[marker].majorAllele();
+ }
+
+ @Override
+ public int alleleCount(int marker, int allele) {
+ return refVcfRecs[marker].alleleCount(allele);
+ }
+
+ @Override
+ public int hapIndex(int marker, int allele, int copy) {
+ return refVcfRecs[marker].hapIndex(allele, copy);
+ }
+}
diff --git a/ibd/HaploidIbd.java b/ibd/HaploidIbd.java
index 925593d..e4575e2 100644
--- a/ibd/HaploidIbd.java
+++ b/ibd/HaploidIbd.java
@@ -1,264 +1,264 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package ibd;
-
-import blbutil.IntPair;
-import blbutil.Utilities;
-import dag.Dag;
-import haplotype.HapPairs;
-import haplotype.SampleHapPairs;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import vcf.GL;
-
-/**
- * <p>Class {@code HaploidIbd} implements the Refined IBD algorithm.
- * The Refined IBD algorithm detects candidate haplotype IBD segments with the
- * Germline Algorithm and then evaluates candidate IBD segments using a
- * likelihood ratio test.
- * </p>
- * <p>Instances of class {@code HaploidIbd} are immutable.
- *</p>
- * Reference: Gusev A, Lowe JK, Stoffel M, Daly MJ, Altshuler D, Breslow JL,
- * Friedman JM, Pe'er I. Whole population, genomewide mapping
- * of hidden relatedness. Genome Research 2009;19(2):318-26.
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public final class HaploidIbd {
-
- private final int ibdTrim;
- private final float minIbdLod;
- private final float minIbsLength; // positions from Dag.posArray()
- private final float minFreqLod; // for shared haplotype
-
- /**
- * Constructs a new {@code HaploidIbd} instance from the specified data.
- * @param ibdTrim the number of markers to trim from an IBS segment
- * when computing the IBD versus non-IBD likelihood ratio
- * @param minIbdLod the minimum IBD LOD score of reported IBD segments
- *
- * @throws IllegalArgumentException if {@code ibdTrim < 0 }
- * @throws IllegalArgumentException if
- * {@code ibdLod <= 0.0f || Float.isFinite(ibdLod) == false}
- */
- public HaploidIbd(int ibdTrim, float minIbdLod) {
- if (ibdTrim < 0) {
- throw new IllegalArgumentException("trim: " + ibdTrim);
- }
- if (minIbdLod <= 0.0 || Float.isFinite(minIbdLod) == false) {
- throw new IllegalArgumentException("minIbdlod: " + minIbdLod);
- }
- this.ibdTrim = ibdTrim;
- this.minIbdLod = minIbdLod;
- this.minIbsLength = 0.8f*minIbdLod;
- this.minFreqLod = minIbdLod;
- }
-
- /**
- * Runs the Refined IBD algorithm, and returns a map whose keys are
- * ordered pairs of haplotype indices and whose values are thread-safe
- * lists of IBD segments for each haplotype pair. The minimum haplotype
- * index is listed first in each ordered pair of haplotype indices.
- *
- * @param gl the HMM emission probabilities
- * @param dag the HMM transition probabilities
- * @param haps the sample haplotype pairs
- * @param nThreads the number of threads of execution that may be used
- * @return the detected IBD segments
- *
- * @throws IllegalArgumentException if {@code nThreads < 1}
- * @throws IllegalArgumentException if
- * {@code gl.samples().equals(haps.samples()) == false}
- * @throws IllegalArgumentException if
- * {@code gl.markers().equals(dag.markers()) == false
- || gl.markers().equals(haps.markers()) == false}
- * @throws NullPointerException if
- * {@code gl == null || dag == null || haps == null}
- */
- @SuppressWarnings({"BroadCatchBlock", "TooBroadCatch"})
- public Map<IntPair, List<IbdSegment>> run(GL gl, Dag dag,
- SampleHapPairs haps, final int nThreads) {
- checkParameters(gl, dag, haps);
- double[] pos = dag.posArray();
- IbsHapSegments ibsSegments = new IbsHapSegments(haps, pos, minIbsLength);
- ConcurrentMap<IntPair, List<IbdSegment>> ibdMap
- = new ConcurrentHashMap<>();
-
- final BlockingQueue<Integer> qIn = new ArrayBlockingQueue<>(5*nThreads);
- ExecutorService es = Executors.newFixedThreadPool(nThreads);
- for (int j=0; j<nThreads; ++j) {
- IbdBaum baum = new IbdBaum(dag, gl);
- es.submit(new ProduceIbd(haps, baum, ibsSegments, qIn, ibdMap,
- ibdTrim, minIbdLod));
- }
- try {
- for (int hap=0, n=haps.nHaps(); hap<n; ++hap) {
- qIn.put(hap);
- }
- for (int j=0; j<nThreads; ++j) {
- qIn.put(ProduceIbd.POISON);
- }
- es.shutdown();
- es.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
- }
- catch (Throwable e) {
- Utilities.exit("ERROR", e);
- }
- return ibdMap;
- }
-
- private void checkParameters(GL gl, Dag dag, SampleHapPairs haps) {
- if (gl.samples().equals(haps.samples())==false) {
- throw new IllegalArgumentException("inconstent samples");
- }
- if (gl.markers().equals(dag.markers())==false
- || gl.markers().equals(haps.markers())==false) {
- throw new IllegalArgumentException("inconsistent markers");
- }
- }
-
- private static double freqLod(int hap, int start, int end, int ibdTrim,
- Dag dag, HapPairs haps) {
- int trimmedStart = start + ibdTrim;
- int trimmedEnd = end - ibdTrim;
- if (trimmedStart >= trimmedEnd) {
- return 0.0f;
- }
- else {
- return IbdBaum.freqLod(hap, trimmedStart, trimmedEnd, haps, dag);
- }
- }
-
- private static double ibdLod(IbdBaum ibdBaum, int hap1, int hap2, int start,
- int end, int ibdTrim) {
- int trimmedStart = start + ibdTrim;
- int trimmedEnd = end - ibdTrim;
- if (trimmedStart >= trimmedEnd) {
- return 0.0f;
- }
- else {
- int sample1 = hap1/2;
- int sample2 = hap2/2;
- return ibdBaum.ibdLod(sample1, sample2, trimmedStart, trimmedEnd);
- }
- }
-
- private class ProduceIbd implements Runnable {
-
- public static final int POISON = -37;
-
- private final SampleHapPairs haps;
- private final IbdBaum baum;
- private final IbsHapSegments ibsHapSegments;
- private final BlockingQueue<Integer> qIn;
- private final ConcurrentMap<IntPair, List<IbdSegment>> ibdMap;
- private final int ibdTrim;
- private final float minIbdLod;
-
- public ProduceIbd(SampleHapPairs haps, IbdBaum baum,
- IbsHapSegments ibsHapSegments, BlockingQueue<Integer> qIn,
- ConcurrentMap<IntPair, List<IbdSegment>> ibdMap, int ibdTrim,
- float minIbdLod) {
- if (ibdTrim < 0) {
- throw new IllegalArgumentException("trim < 0: " + ibdTrim);
- }
- if (minIbdLod <= 0.0 || Float.isNaN(minIbdLod)) {
- throw new IllegalArgumentException("ibdlod: " + minIbdLod);
- }
- this.haps = haps;
- this.baum = baum;
- this.ibsHapSegments = ibsHapSegments;
- this.qIn = qIn;
- this.ibdMap = ibdMap;
- this.ibdTrim = ibdTrim;
- this.minIbdLod = minIbdLod;
- }
-
- /*
- * Takes haplotype indices from a thread-safe work-queue and stores
- * detected IBD segments that between the haplotype and
- * haplotypes with larger index in {@code this.ibdMap}. The method
- * exits when {@code ProduceSingleSamples.POISON} is taken from the
- * work queue.
- *
- * @throws IndexOutOfBounds exception if a negative integer
- * other than {@code ProduceSingleSamples.POISON} is taken from the
- * work queue
- */
- @Override
- @SuppressWarnings({"BroadCatchBlock", "TooBroadCatch"})
- public void run() {
- try {
- int hap = qIn.take();
- while (hap!=POISON) {
- List<HapSegment> ibsSegs = ibsHapSegments.find(hap);
- for (int j=0, n=ibsSegs.size(); j<n; ++j) {
- HapSegment hs = ibsSegs.get(j);
- if (hap < hs.hap()) {
- int start = hs.start();
- int end = hs.end();
- double freqLod = HaploidIbd.freqLod(hap, start,
- (end+1), ibdTrim, baum.dag(), haps);
- if (freqLod >= minFreqLod) {
- float ibdLod;
- if ( (hap/2) == (hs.hap()/2) ) {
- int sample = hap/2;
- ibdLod = (float) baum.hbdLod(sample, start, (end+1));
- }
- else {
- ibdLod = (float) HaploidIbd.ibdLod(baum, hap,
- hs.hap(), start, (end+1), ibdTrim);
- }
- if (ibdLod >= minIbdLod) {
- IntPair hapPair = new IntPair(hap, hs.hap());
- List<IbdSegment> list = ibdMap.get(hapPair);
- if (list==null) {
- list = Collections.synchronizedList(
- new ArrayList<IbdSegment>(2));
- ibdMap.putIfAbsent(hapPair, list);
- list = ibdMap.get(hapPair);
- }
- IbdSegment segment = new IbdSegment(hapPair,
- baum.gl().marker(start),
- baum.gl().marker(end),
- ibdLod, start, end );
- list.add(segment);
- }
- }
- }
- }
- hap = qIn.take();
- }
- }
- catch (Throwable e) {
- Utilities.exit("ProduceSingleSamples: ERROR", e);
- }
- }
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package ibd;
+
+import blbutil.IntPair;
+import blbutil.Utilities;
+import dag.Dag;
+import haplotype.HapPairs;
+import haplotype.SampleHapPairs;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import vcf.GL;
+
+/**
+ * <p>Class {@code HaploidIbd} implements the Refined IBD algorithm.
+ * The Refined IBD algorithm detects candidate haplotype IBD segments with the
+ * Germline Algorithm and then evaluates candidate IBD segments using a
+ * likelihood ratio test.
+ * </p>
+ * <p>Instances of class {@code HaploidIbd} are immutable.
+ *</p>
+ * Reference: Gusev A, Lowe JK, Stoffel M, Daly MJ, Altshuler D, Breslow JL,
+ * Friedman JM, Pe'er I. Whole population, genomewide mapping
+ * of hidden relatedness. Genome Research 2009;19(2):318-26.
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public final class HaploidIbd {
+
+ private final int ibdTrim;
+ private final float minIbdLod;
+ private final float minIbsLength; // positions from Dag.posArray()
+ private final float minFreqLod; // for shared haplotype
+
+ /**
+ * Constructs a new {@code HaploidIbd} instance from the specified data.
+ * @param ibdTrim the number of markers to trim from an IBS segment
+ * when computing the IBD versus non-IBD likelihood ratio
+ * @param minIbdLod the minimum IBD LOD score of reported IBD segments
+ *
+ * @throws IllegalArgumentException if {@code ibdTrim < 0 }
+ * @throws IllegalArgumentException if
+ * {@code ibdLod <= 0.0f || Float.isFinite(ibdLod) == false}
+ */
+ public HaploidIbd(int ibdTrim, float minIbdLod) {
+ if (ibdTrim < 0) {
+ throw new IllegalArgumentException("trim: " + ibdTrim);
+ }
+ if (minIbdLod <= 0.0 || Float.isFinite(minIbdLod) == false) {
+ throw new IllegalArgumentException("minIbdlod: " + minIbdLod);
+ }
+ this.ibdTrim = ibdTrim;
+ this.minIbdLod = minIbdLod;
+ this.minIbsLength = 0.8f*minIbdLod;
+ this.minFreqLod = minIbdLod;
+ }
+
+ /**
+ * Runs the Refined IBD algorithm, and returns a map whose keys are
+ * ordered pairs of haplotype indices and whose values are thread-safe
+ * lists of IBD segments for each haplotype pair. The minimum haplotype
+ * index is listed first in each ordered pair of haplotype indices.
+ *
+ * @param gl the HMM emission probabilities
+ * @param dag the HMM transition probabilities
+ * @param haps the sample haplotype pairs
+ * @param nThreads the number of threads of execution that may be used
+ * @return the detected IBD segments
+ *
+ * @throws IllegalArgumentException if {@code nThreads < 1}
+ * @throws IllegalArgumentException if
+ * {@code gl.samples().equals(haps.samples()) == false}
+ * @throws IllegalArgumentException if
+ * {@code gl.markers().equals(dag.markers()) == false
+ || gl.markers().equals(haps.markers()) == false}
+ * @throws NullPointerException if
+ * {@code gl == null || dag == null || haps == null}
+ */
+ @SuppressWarnings({"BroadCatchBlock", "TooBroadCatch"})
+ public Map<IntPair, List<IbdSegment>> run(GL gl, Dag dag,
+ SampleHapPairs haps, final int nThreads) {
+ checkParameters(gl, dag, haps);
+ double[] pos = dag.posArray();
+ IbsHapSegments ibsSegments = new IbsHapSegments(haps, pos, minIbsLength);
+ ConcurrentMap<IntPair, List<IbdSegment>> ibdMap
+ = new ConcurrentHashMap<>();
+
+ final BlockingQueue<Integer> qIn = new ArrayBlockingQueue<>(5*nThreads);
+ ExecutorService es = Executors.newFixedThreadPool(nThreads);
+ for (int j=0; j<nThreads; ++j) {
+ IbdBaum baum = new IbdBaum(dag, gl);
+ es.submit(new ProduceIbd(haps, baum, ibsSegments, qIn, ibdMap,
+ ibdTrim, minIbdLod));
+ }
+ try {
+ for (int hap=0, n=haps.nHaps(); hap<n; ++hap) {
+ qIn.put(hap);
+ }
+ for (int j=0; j<nThreads; ++j) {
+ qIn.put(ProduceIbd.POISON);
+ }
+ es.shutdown();
+ es.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
+ }
+ catch (Throwable e) {
+ Utilities.exit("ERROR", e);
+ }
+ return ibdMap;
+ }
+
+ private void checkParameters(GL gl, Dag dag, SampleHapPairs haps) {
+ if (gl.samples().equals(haps.samples())==false) {
+ throw new IllegalArgumentException("inconstent samples");
+ }
+ if (gl.markers().equals(dag.markers())==false
+ || gl.markers().equals(haps.markers())==false) {
+ throw new IllegalArgumentException("inconsistent markers");
+ }
+ }
+
+ private static double freqLod(int hap, int start, int end, int ibdTrim,
+ Dag dag, HapPairs haps) {
+ int trimmedStart = start + ibdTrim;
+ int trimmedEnd = end - ibdTrim;
+ if (trimmedStart >= trimmedEnd) {
+ return 0.0f;
+ }
+ else {
+ return IbdBaum.freqLod(hap, trimmedStart, trimmedEnd, haps, dag);
+ }
+ }
+
+ private static double ibdLod(IbdBaum ibdBaum, int hap1, int hap2, int start,
+ int end, int ibdTrim) {
+ int trimmedStart = start + ibdTrim;
+ int trimmedEnd = end - ibdTrim;
+ if (trimmedStart >= trimmedEnd) {
+ return 0.0f;
+ }
+ else {
+ int sample1 = hap1/2;
+ int sample2 = hap2/2;
+ return ibdBaum.ibdLod(sample1, sample2, trimmedStart, trimmedEnd);
+ }
+ }
+
+ private class ProduceIbd implements Runnable {
+
+ public static final int POISON = -37;
+
+ private final SampleHapPairs haps;
+ private final IbdBaum baum;
+ private final IbsHapSegments ibsHapSegments;
+ private final BlockingQueue<Integer> qIn;
+ private final ConcurrentMap<IntPair, List<IbdSegment>> ibdMap;
+ private final int ibdTrim;
+ private final float minIbdLod;
+
+ public ProduceIbd(SampleHapPairs haps, IbdBaum baum,
+ IbsHapSegments ibsHapSegments, BlockingQueue<Integer> qIn,
+ ConcurrentMap<IntPair, List<IbdSegment>> ibdMap, int ibdTrim,
+ float minIbdLod) {
+ if (ibdTrim < 0) {
+ throw new IllegalArgumentException("trim < 0: " + ibdTrim);
+ }
+ if (minIbdLod <= 0.0 || Float.isNaN(minIbdLod)) {
+ throw new IllegalArgumentException("ibdlod: " + minIbdLod);
+ }
+ this.haps = haps;
+ this.baum = baum;
+ this.ibsHapSegments = ibsHapSegments;
+ this.qIn = qIn;
+ this.ibdMap = ibdMap;
+ this.ibdTrim = ibdTrim;
+ this.minIbdLod = minIbdLod;
+ }
+
+ /*
+ * Takes haplotype indices from a thread-safe work-queue and stores
+ * detected IBD segments that between the haplotype and
+ * haplotypes with larger index in {@code this.ibdMap}. The method
+ * exits when {@code ProduceSingleSamples.POISON} is taken from the
+ * work queue.
+ *
+ * @throws IndexOutOfBounds exception if a negative integer
+ * other than {@code ProduceSingleSamples.POISON} is taken from the
+ * work queue
+ */
+ @Override
+ @SuppressWarnings({"BroadCatchBlock", "TooBroadCatch"})
+ public void run() {
+ try {
+ int hap = qIn.take();
+ while (hap!=POISON) {
+ List<HapSegment> ibsSegs = ibsHapSegments.find(hap);
+ for (int j=0, n=ibsSegs.size(); j<n; ++j) {
+ HapSegment hs = ibsSegs.get(j);
+ if (hap < hs.hap()) {
+ int start = hs.start();
+ int end = hs.end();
+ double freqLod = HaploidIbd.freqLod(hap, start,
+ (end+1), ibdTrim, baum.dag(), haps);
+ if (freqLod >= minFreqLod) {
+ float ibdLod;
+ if ( (hap/2) == (hs.hap()/2) ) {
+ int sample = hap/2;
+ ibdLod = (float) baum.hbdLod(sample, start, (end+1));
+ }
+ else {
+ ibdLod = (float) HaploidIbd.ibdLod(baum, hap,
+ hs.hap(), start, (end+1), ibdTrim);
+ }
+ if (ibdLod >= minIbdLod) {
+ IntPair hapPair = new IntPair(hap, hs.hap());
+ List<IbdSegment> list = ibdMap.get(hapPair);
+ if (list==null) {
+ list = Collections.synchronizedList(
+ new ArrayList<IbdSegment>(2));
+ ibdMap.putIfAbsent(hapPair, list);
+ list = ibdMap.get(hapPair);
+ }
+ IbdSegment segment = new IbdSegment(hapPair,
+ baum.gl().marker(start),
+ baum.gl().marker(end),
+ ibdLod, start, end );
+ list.add(segment);
+ }
+ }
+ }
+ }
+ hap = qIn.take();
+ }
+ }
+ catch (Throwable e) {
+ Utilities.exit("ProduceSingleSamples: ERROR", e);
+ }
+ }
+ }
+}
diff --git a/main/ConstrainedAlleleProbs.java b/main/ConstrainedAlleleProbs.java
index cfa050e..420acd1 100644
--- a/main/ConstrainedAlleleProbs.java
+++ b/main/ConstrainedAlleleProbs.java
@@ -81,25 +81,38 @@ public class ConstrainedAlleleProbs implements AlleleProbs {
this.indexMap = indexMap.clone();
}
+ /**
+ * Returns {@code true} if the specified marker is not present in the
+ * input data and returns {@code false} otherwise.
+ * @param marker a marker index
+ * @return {@code true} if the specified marker is not present in the
+ * input target data
+ * @throws IndexOutOfBoundsException if
+ * {@code marker < 0 || marker >= this.nMarkers()}
+ */
+ public boolean isImputed(int marker) {
+ return indexMap[marker] == -1;
+ }
+
@Override
public float alProb1(int marker, int sample, int allele) {
- int glMarker = indexMap[marker];
- if (glMarker == -1) {
+ int targetMarker = indexMap[marker];
+ if (targetMarker == -1) {
return alProbs.alProb1(marker, sample, allele);
}
else {
- return shp.allele1(glMarker, sample) == allele ? 1f : 0f;
+ return shp.allele1(targetMarker, sample) == allele ? 1f : 0f;
}
}
@Override
public float alProb2(int marker, int sample, int allele) {
- int glMarker = indexMap[marker];
- if (glMarker == -1) {
+ int targetMarker = indexMap[marker];
+ if (targetMarker == -1) {
return alProbs.alProb2(marker, sample, allele);
}
else {
- return shp.allele2(glMarker, sample) == allele ? 1f : 0f;
+ return shp.allele2(targetMarker, sample) == allele ? 1f : 0f;
}
}
@@ -110,23 +123,23 @@ public class ConstrainedAlleleProbs implements AlleleProbs {
@Override
public int allele1(int marker, int sample) {
- int glMarker = indexMap[marker];
- if (glMarker == -1) {
+ int targetMarker = indexMap[marker];
+ if (targetMarker == -1) {
return alProbs.allele1(marker, sample);
}
else {
- return shp.allele1(glMarker, sample);
+ return shp.allele1(targetMarker, sample);
}
}
@Override
public int allele2(int marker, int sample) {
- int glMarker = indexMap[marker];
- if (glMarker == -1) {
+ int targetMarker = indexMap[marker];
+ if (targetMarker == -1) {
return alProbs.allele2(marker, sample);
}
else {
- return shp.allele2(glMarker, sample);
+ return shp.allele2(targetMarker, sample);
}
}
diff --git a/main/Main.java b/main/Main.java
index 9ba5d4c..c072110 100644
--- a/main/Main.java
+++ b/main/Main.java
@@ -65,8 +65,8 @@ public class Main {
/**
* The program name and version.
*/
- public static final String program = "beagle.22Feb16.8ef.jar (version 4.1)";
- public static final String command = "java -jar beagle.22Feb16.8ef.jar";
+ public static final String program = "beagle.03May16.862.jar (version 4.1)";
+ public static final String command = "java -jar beagle.03May16.862.jar";
/**
* The copyright string.
@@ -78,7 +78,7 @@ public class Main {
*/
public static final String shortHelp = Main.program
+ Const.nl + Main.copyright
- + Const.nl + "Enter \"java -jar beagle.22Feb16.8ef.jar\" for a "
+ + Const.nl + "Enter \"java -jar beagle.03May16.862.jar\" for a "
+ "summary of command line " + "arguments.";
private final Par par;
@@ -194,15 +194,7 @@ public class Main {
private void printOutput(CurrentData cd, SampleHapPairs targetHapPairs,
AlleleProbs alProbs, Map<IntPair, List<IbdSegment>> ibd) {
assert par.gt()!=null;
- boolean markersAreImputed = false;
- boolean printGprobs = false;
- if (cd.nTargetMarkers() < cd.nMarkers()){
- alProbs = new ConstrainedAlleleProbs(targetHapPairs, alProbs,
- cd.targetMarkerIndices());
- markersAreImputed = true;
- printGprobs = par.gprobs();
- }
- windowOut.print(cd, alProbs, markersAreImputed, printGprobs);
+ windowOut.print(cd, targetHapPairs, alProbs, par.gprobs());
if (par.ibd()) {
windowOut.printIbd(cd, ibd);
}
diff --git a/main/WindowWriter.java b/main/WindowWriter.java
index eb3a21a..ee0d794 100644
--- a/main/WindowWriter.java
+++ b/main/WindowWriter.java
@@ -22,6 +22,7 @@ import beagleutil.Samples;
import blbutil.Const;
import blbutil.FileUtil;
import blbutil.IntPair;
+import haplotype.SampleHapPairs;
import ibd.IbdSegment;
import java.io.Closeable;
import java.io.File;
@@ -140,11 +141,10 @@ public class WindowWriter implements Closeable {
* {@code cd.nextSplice()} (exclusive).
*
* @param cd the input data for the current marker window
+ * @param targetHapPairs the target haplotype pairs
* @param alProbs the estimated haplotype allele probabilities
- * @param imputed {@code true} if there are imputed markers,
- * and {@code false} otherwise
- * @param gprobs {@code true} if the GP field should be printed, and
- * {@code false} otherwise
+ * @param gprobs {@code true} if the GP field should be printed when
+ * imputed markers are present, and {@code false} otherwise
*
* @throws IllegalStateException if {@code this.isClosed() == true}
* @throws IllegalArgumentException if
@@ -153,10 +153,11 @@ public class WindowWriter implements Closeable {
* {@code this.samples().equals(alProbs.samples()) == false}
* @throws IllegalArgumentException if
* {@code cd.markers().equals(alProbs.markers()) == false}
- * @throws NullPointerException if {@code cd == null || alProbs == null}
+ * @throws NullPointerException if
+ * {@code cd == null || targetHapPairs == null || alProbs == null}
*/
- public void print(CurrentData cd, AlleleProbs alProbs, boolean imputed,
- boolean gprobs) {
+ public void print(CurrentData cd, SampleHapPairs targetHapPairs,
+ AlleleProbs alProbs, boolean gprobs) {
if (isClosed) {
throw new IllegalStateException("isClosed()==true");
}
@@ -169,7 +170,14 @@ public class WindowWriter implements Closeable {
}
int start = cd.prevSplice();
int end = cd.nextSplice();
- VcfWriter.appendRecords(alProbs, start, end, imputed, gprobs, vcfOut);
+ if (cd.nTargetMarkers() < cd.nMarkers()){
+ ConstrainedAlleleProbs constAlProbs = new ConstrainedAlleleProbs(
+ targetHapPairs, alProbs, cd.targetMarkerIndices());
+ VcfWriter.appendRecords(constAlProbs, start, end, gprobs, vcfOut);
+ }
+ else {
+ VcfWriter.appendRecords(alProbs, start, end, vcfOut);
+ }
vcfOut.flush();
}
diff --git a/sample/DuoBaumLevel.java b/sample/DuoBaumLevel.java
index 163a909..8e794bb 100644
--- a/sample/DuoBaumLevel.java
+++ b/sample/DuoBaumLevel.java
@@ -1,641 +1,641 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package sample;
-
-import dag.Dag;
-import java.util.Arrays;
-import vcf.BasicGL;
-import vcf.GL;
-
-/**
- * <p>Class {@code DuoBaumLevel} computes forward and backward Baum
- * values at a level of a hidden Markov model (HMM) whose states are
- * ordered edge trios of a leveled directed acyclic graph (DAG).
- * </p>
- * <p>Instances of class {@code SingleBaumLevel} are not thread-safe.
- * </p>
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class DuoBaumLevel {
-
- private static final int INITIAL_CAPACITY = 400;
- private static final float MIN_VALUE = 100*Float.MIN_VALUE;
- private final Dag dag;
- private final GL gl;
-
- private int marker = -1;
- private int sampleA = -1;
- private int sampleB = -1;
- private int size = 0;
-
- private int capacity = INITIAL_CAPACITY;
- private int[] edgesAB1 = new int[INITIAL_CAPACITY];
- private int[] edgesA2 = new int[INITIAL_CAPACITY];
- private int[] edgesB2 = new int[INITIAL_CAPACITY];
- private float[] fwdValues = new float[INITIAL_CAPACITY];
- private float[] bwdValues = new float[INITIAL_CAPACITY];
- private float fwdValueSum = 0f;
- private float bwdValueSum = 0f;
-
- private int nGenotypes = 0;
- private float[] gtProbsA = new float[3];
- private float[] gtProbsB = new float[3];
-
- /**
- * Constructs a new {@code DuoBaumLevel} instance from the specified data.
- * @param dag the directed acyclic graph that the determines transition
- * probabilities
- * @param gl the emission probabilities
- * @throws IllegalArgumentException if
- * {@code dag.markers().equals(gl.markers()) == false}
- * @throws NullPointerException if {@code dag == null || gl == null}
- */
- public DuoBaumLevel(Dag dag, GL gl) {
- if (dag.markers().equals(gl.markers())==false) {
- throw new IllegalArgumentException("marker inconsistency");
- }
- this.dag = dag;
- this.gl = gl;
- }
-
- /**
- * Sets the Baum forward algorithm values for this level of the HMM
- * and records the child node trio values in the specified
- * {@code nodes} parameter. When the method call returns, the {@code nodes}
- * parameter will be reset to the child node trio values for this level of
- * the HMM.
- *
- * @param nodes child node trio values at the previous level of the HMM
- * @param marker the level of the HMM at which the Baum forward algorithm
- * probabilities will be computed
- * @param sampleA the parent sample index
- * @param sampleB the offspring sample index
- *
- * @throws IndexOutOfBoundsException if
- * {@code marker < 0 || marker >= this.dag().nMarkers()}
- * @throws IndexOutOfBoundsException if
- * {@code sampleA < 0 || sampleA >= this.gl().nSamples()}
- * @throws IndexOutOfBoundsException if
- * {@code sampleB < 0 || sampleB >= this.gl().nSamples()}
- * @throws IndexOutOfBoundsException if any node in any node trio with
- * non-zero value is not a valid parent node at the specified level of the
- * HMM
- * @throws NullPointerException if {@code nodes == null}
- */
- public void setForwardValues(DuoNodes nodes, int marker, int sampleA,
- int sampleB) {
- this.marker = marker;
- this.sampleA = sampleA;
- this.sampleB = sampleB;
- this.nGenotypes = gl.marker(marker).nGenotypes();
- this.size = 0;
- this.fwdValueSum = 0f;
- this.bwdValueSum = 0f;
- initializeGtProbs(); // initialized here due to gtProbs() contract
- setStates(nodes);
- setChildNodes(nodes);
- }
-
- private void initializeGtProbs() {
- if (gtProbsA.length < nGenotypes) {
- int newLength = Math.max(nGenotypes, (3*gtProbsA.length/2 + 1));
- gtProbsA = new float[newLength];
- gtProbsB = new float[newLength];
- }
- else {
- for (int j=0; j<nGenotypes; ++j) {
- gtProbsA[j] = 0f;
- gtProbsB[j] = 0f;
- }
- }
- }
-
- private void setStates(DuoNodes nodes) {
- float valueSum = 0f;
- for (int j=0, n=nodes.size(); j<n; ++j) {
- int nodeAB1 = nodes.enumNodeAB1(j);
- int nodeA2 = nodes.enumNodeA2(j);
- int nodeB2 = nodes.enumNodeB2(j);
- float nodeValue = nodes.enumValue(j);
- for (int ab1=0, nAB1=dag.nOutEdges(marker, nodeAB1); ab1<nAB1; ++ab1) {
- int edgeAB1 = dag.outEdge(marker, nodeAB1, ab1);
- int symbolAB1 = dag.symbol(marker, edgeAB1);
- for (int a2=0, nA2=dag.nOutEdges(marker, nodeA2); a2<nA2; ++a2) {
- int edgeA2 = dag.outEdge(marker, nodeA2, a2);
- int symbolA2 = dag.symbol(marker, edgeA2);
- float epA = gl.gl(marker, sampleA, symbolAB1, symbolA2);
- if (epA > 0.0) {
- for (int b2=0, nB2=dag.nOutEdges(marker, nodeB2); b2<nB2; ++b2) {
- int edgeB2 = dag.outEdge(marker, nodeB2, b2);
- int symbolB2 = dag.symbol(marker, edgeB2);
- float epB = gl.gl(marker, sampleB, symbolAB1, symbolB2);
- if (epB > 0.0) {
- if (size == capacity) {
- ensureCapacity(size+1);
- }
- float tpAB1 = dag.condEdgeProb(marker, edgeAB1);
- float tpA2 = dag.condEdgeProb(marker, edgeA2);
- float tpB2 = dag.condEdgeProb(marker, edgeB2);
- float fwdValue = (epA * epB) * nodeValue
- * (tpAB1 * tpA2 * tpB2);
- if (fwdValue<MIN_VALUE && nodeValue > 0.0) {
- fwdValue = MIN_VALUE;
- }
- edgesAB1[size] = edgeAB1;
- edgesA2[size] = edgeA2;
- edgesB2[size] = edgeB2;
- fwdValues[size++] = fwdValue;
- valueSum += fwdValue;
- }
- }
- }
- }
- }
- }
- assert valueSum>0.0 ^ size==0;
- for (int k=0; k<size; ++k) {
- this.fwdValues[k] /= valueSum;
- }
- fwdValueSum = valueSum;
- }
-
- /**
- * Stores the Baum forward algorithm child node trio values for this
- * level of the HMM in the specified {@code DuoNodes} object.
- *
- * @param nodes the node trio values that will be set
- *
- * @throws NullPointerException if {@code nodes == null}
- */
- public void setChildNodes(DuoNodes nodes) {
- nodes.clear();
- for (int k=0; k<size; ++k) {
- int nodeAB1 = dag.childNode(marker, edgesAB1[k]);
- int nodeA2 = dag.childNode(marker, edgesA2[k]);
- int nodeB2 = dag.childNode(marker, edgesB2[k]);
- nodes.sumUpdate(nodeAB1, nodeA2, nodeB2, fwdValues[k]);
- }
- }
-
- /**
- * Sets the Baum backward algorithm values for this level of the HMM
- * and stores the parent node trio values in the specified
- * {@code nodes} parameter. When the method call returns, the
- * ${@code nodes} parameter will be reset to the parent node trio values
- * for this level of the HMM.
- *
- * @param nodes parent node trio values at the next level of HMM
- *
- * @throws IndexOutOfBoundsException if any node in any node trio with
- * non-zero value is not a valid child node at this level of the HMM
- * @throws NullPointerException if {@code nodes == null}
- */
- public void setBackwardValues(DuoNodes nodes) {
- for (int j=0; j<size; ++j) {
- int nodeAB1 = dag.childNode(marker, edgesAB1[j]);
- int nodeA2 = dag.childNode(marker, edgesA2[j]);
- int nodeB2 = dag.childNode(marker, edgesB2[j]);
- float backwardValue = nodes.value(nodeAB1, nodeA2, nodeB2);
- bwdValues[j] = backwardValue;
- bwdValueSum += backwardValue;
- }
- nodes.clear();
- float gtProbsSum = 0f;
- for (int j=0; j<size; ++j) {
- bwdValues[j] /= bwdValueSum;
- int symbolAB1 = symbolAB1(j);
- int symbolA2 = symbolA2(j);
- int symbolB2 = symbolB2(j);
- float tpAB1 = dag.condEdgeProb(marker, edgesAB1[j]);
- float tpA2 = dag.condEdgeProb(marker, edgesA2[j]);
- float tpB2 = dag.condEdgeProb(marker, edgesB2[j]);
-
- float stateProb = fwdValues[j] * bwdValues[j];
- int gtIndexA = BasicGL.genotype(symbolAB1, symbolA2);
- int gtIndexB = BasicGL.genotype(symbolAB1, symbolB2);
- // gtProbs[AB] assumed to be initialized in setForwardValues() method
- gtProbsA[gtIndexA] += stateProb;
- gtProbsB[gtIndexB] += stateProb;
- gtProbsSum += stateProb;
-
- float epA = gl.gl(marker, sampleA, symbolAB1, symbolA2);
- float epB = gl.gl(marker, sampleB, symbolAB1, symbolB2);
- float bwdValue = bwdValues[j] * (tpAB1 * tpA2 * tpB2) * (epA*epB);
- if (bwdValue < MIN_VALUE && bwdValues[j] > 0f) {
- bwdValue = MIN_VALUE;
- }
- int pnAB1 = dag.parentNode(marker, edgesAB1[j]);
- int pnA2 = dag.parentNode(marker, edgesA2[j]);
- int pnB2 = dag.parentNode(marker, edgesB2[j]);
- nodes.sumUpdate(pnAB1, pnA2, pnB2, bwdValue);
- }
- for (int j=0; j<nGenotypes; ++j) {
- gtProbsA[j] /= gtProbsSum;
- gtProbsB[j] /= gtProbsSum;
- }
- }
-
- /**
- * Returns the directed acyclic graph that determines the transition
- * probabilities.
- * @return the directed acyclic graph that determines the transition
- * probabilities
- */
- public Dag dag() {
- return dag;
- }
-
- /**
- * Returns the emission probabilities.
- * @return the emission probabilities
- */
- public GL gl() {
- return gl;
- }
-
- /**
- * Return the level of the HMM.
- * @return the level of the HMM
- */
- public int marker() {
- return marker;
- }
-
- /**
- * Return the number of possible genotypes at this level of the HMM.
- * @return the number of possible genotypes at this level of the HMM
- */
- public int nGenotypes() {
- return nGenotypes;
- }
-
- /**
- * Returns the specified posterior genotype probability for the parent.
- * Returns 0 if the Baum backward probabilities have not been set.
- * @param gt a genotype index
- * @return the specified posterior genotype probability for the parent
- * @throws IndexOutOfBoundsException if
- * {@code gt < 0 || gt >= this.nGenotypes()}
- */
- public float gtProbsA(int gt) {
- checkGT(gt);
- return gtProbsA[gt];
- }
-
- /**
- * Returns the specified posterior genotype probability for the offspring.
- * Returns 0 if the Baum backward probabilities have not been set.
- * @param gt a genotype index
- * @return the specified posterior genotype probability for the offspring
- * @throws IndexOutOfBoundsException if
- * {@code gt < 0 || gt >= this.nGenotypes()}
- */
- public float gtProbsB(int gt) {
- checkGT(gt);
- return gtProbsB[gt];
- }
-
- private void checkGT(int gt) {
- if (gt >= nGenotypes) {
- throw new IllegalArgumentException(String.valueOf(gt));
- }
- }
-
- /**
- * Return the number of states with nonzero forward probability at
- * this level of the HMM.
- *
- * @return the number of states with nonzero forward probability at
- * this level of the HMM
- */
- public int size() {
- return size;
- }
-
- private void checkIndex(int state) {
- if (state >= size) {
- throw new IndexOutOfBoundsException(String.valueOf(size));
- }
- }
-
- /**
- * Returns the first edge of the specified HMM state with nonzero forward
- * probability.
- * @param state an index of a HMM state with nonzero forward probability
- * @return the first edge of the specified HMM state with nonzero forward
- * probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int edgeAB1(int state) {
- checkIndex(state);
- return edgesAB1[state];
- }
-
- /**
- * Returns the second edge of the specified HMM state with nonzero forward
- * probability.
- * @param state an index of a HMM state with nonzero forward probability
- * @return the second edge of the specified HMM state with nonzero forward
- * probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int edgeA2(int state) {
- checkIndex(state);
- return edgesA2[state];
- }
-
- /**
- * Returns the third edge of the specified HMM state with nonzero forward
- * probability.
- * @param state an index of a HMM state with nonzero forward probability
- * @return the third edge of the specified HMM state with nonzero forward
- * probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int edgeB2(int state) {
- checkIndex(state);
- return edgesB2[state];
- }
-
- /**
- * Returns the parent node of the first edge of the specified HMM state
- * with nonzero forward probability.
- *
- * @param state an index of a HMM state with nonzero forward probability
- * @return the parent node of the first edge of the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int parentNodeAB1(int state) {
- checkIndex(state);
- return dag.parentNode(marker, edgesAB1[state]);
- }
-
- /**
- * Returns the parent node of the second edge of the specified HMM state
- * with nonzero forward probability.
- *
- * @param state an index of a HMM state with nonzero forward probability
- * @return the parent node of the second edge of the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int parentNodeA2(int state) {
- checkIndex(state);
- return dag.parentNode(marker, edgesA2[state]);
- }
-
- /**
- * Returns the parent node of the third edge of the specified HMM state
- * with nonzero forward probability.
- *
- * @param state an index of a HMM state with nonzero forward probability
- * @return the parent node of the third edge of the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int parentNodeB2(int state) {
- checkIndex(state);
- return dag.parentNode(marker, edgesB2[state]);
- }
-
- /**
- * Returns the child node of the first edge of the specified HMM state
- * with nonzero forward probability.
- *
- * @param state an index of a HMM state with nonzero forward probability
- * @return the child node of the first edge of the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int childNodeAB1(int state) {
- checkIndex(state);
- return dag.childNode(marker, edgesAB1[state]);
- }
-
- /**
- * Returns the child node of the second edge of the specified HMM state
- * with nonzero forward probability.
- *
- * @param state an index of a HMM state with nonzero forward probability
- * @return the child node of the second edge of the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int childNodeA2(int state) {
- checkIndex(state);
- return dag.childNode(marker, edgesA2[state]);
- }
-
- /**
- * Returns the child node of the third edge of the specified HMM state
- * with nonzero forward probability.
- *
- * @param state an index of a HMM state with nonzero forward probability
- * @return the child node of the third edge of the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int childNodeB2(int state) {
- checkIndex(state);
- return dag.childNode(marker, edgesB2[state]);
- }
-
- /**
- * Returns the symbol for the first edge of the specified HMM state
- * with nonzero forward probability.
- *
- * @param state an index of a HMM state with nonzero forward probability
- * @return the symbol for the first edge of the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int symbolAB1(int state) {
- return dag.symbol(marker, edgeAB1(state));
- }
-
- /**
- * Returns the symbol for the second edge of the specified HMM state
- * with nonzero forward probability.
- *
- * @param state an index of a HMM state with nonzero forward probability
- * @return the symbol for the second edge of the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int symbolA2(int state) {
- return dag.symbol(marker, edgeA2(state));
- }
-
- /**
- * Returns the symbol for the third edge of the specified HMM state
- * with nonzero forward probability.
- *
- * @param state an index of a HMM state with nonzero forward probability
- * @return the symbol for the third edge of the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public int symbolB2(int state) {
- return dag.symbol(marker, edgeB2(state));
- }
-
- /**
- * Returns the normalized forward value for the specified HMM state
- * with nonzero forward probability.
- * The normalized forward value is obtained by dividing the
- * forward value by the sum of the forward values at this level
- * of the HMM.
- *
- * @param state an index of a HMM state with nonzero forward probability
- *
- * @return the normalized forward value for the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public float forwardValue(int state) {
- checkIndex(state);
- return fwdValues[state];
- }
-
- /**
- * Returns the normalized backward value for the specified HMM state
- * with nonzero forward probability.
- * The normalized backward value is obtained by dividing the
- * backward value by the sum of the backward values at this level
- * of the HMM.
- *
- * @param state an index of a state with nonzero forward probability
- *
- * @return the normalized backward value for the specified HMM state
- * with nonzero forward probability
- *
- * @throws IndexOutOfBoundsException if
- * {@code state < 0 || state >= this.size()}
- */
- public float backwardValue(int state) {
- checkIndex(state);
- return bwdValues[state];
- }
-
- /**
- * Returns the sum of the forward values at this level of the HMM
- * when the forward values are computed using forward values
- * from the previous level that are normalized to sum to 1.
- * @return the sum of the forward values at this level of the HMM
- */
- public float forwardValuesSum() {
- return fwdValueSum;
- }
-
- /**
- * Returns the sum of the backward values at this level of the HMM
- * when the backward values are computed using backward
- * values from the next level that are normalized to sum to 1.
- * @return the sum of the backward values at this level of the HMM
- */
- public float backwardValuesSum() {
- return bwdValueSum;
- }
-
- /**
- * Returns a string description of {@code this}. The exact details
- * of the description are unspecified and subject to change.
- *
- * @return a string description of {@code this}.
- */
- @Override
- public String toString() {
- String space = " ";
- String sep = " | ";
- StringBuilder sb = new StringBuilder(100);
- sb.append("level=");
- sb.append(marker);
- sb.append(" size=");
- sb.append(size);
- sb.append(" forwardValuesSum=");
- sb.append(fwdValueSum);
- sb.append(" backwardSum=");
- sb.append(bwdValueSum);
- for (int j=0; j<size; ++j) {
- sb.append(sep);
- sb.append("j=");
- sb.append(j);
- sb.append(": ");
- sb.append( (int) edgeAB1(j));
- sb.append(space);
- sb.append( (int) edgeA2(j));
- sb.append(space);
- sb.append( (int) edgeB2(j));
- sb.append(space);
- sb.append(forwardValue(j));
- sb.append(space);
- sb.append(backwardValue(j));
- }
- sb.append(sep);
- return sb.toString();
- }
-
- /*
- * Increases the state capacity of array fields as necessary
- * to be greater than or equal to the specified minimum capacity.
- *
- * @param minCapacity the desired minimum state capacity
- */
- private void ensureCapacity(int minCapacity) {
- if (minCapacity > capacity) {
- capacity = (capacity * 3)/2 + 1;
- if (capacity < minCapacity) {
- capacity = minCapacity;
- }
- edgesAB1 = Arrays.copyOf(edgesAB1, capacity);
- edgesA2 = Arrays.copyOf(edgesA2, capacity);
- edgesB2 = Arrays.copyOf(edgesB2, capacity);
- fwdValues = Arrays.copyOf(fwdValues, capacity);
- bwdValues = Arrays.copyOf(bwdValues, capacity);
- }
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package sample;
+
+import dag.Dag;
+import java.util.Arrays;
+import vcf.BasicGL;
+import vcf.GL;
+
+/**
+ * <p>Class {@code DuoBaumLevel} computes forward and backward Baum
+ * values at a level of a hidden Markov model (HMM) whose states are
+ * ordered edge trios of a leveled directed acyclic graph (DAG).
+ * </p>
+ * <p>Instances of class {@code SingleBaumLevel} are not thread-safe.
+ * </p>
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class DuoBaumLevel {
+
+ private static final int INITIAL_CAPACITY = 400;
+ private static final float MIN_VALUE = 100*Float.MIN_VALUE;
+ private final Dag dag;
+ private final GL gl;
+
+ private int marker = -1;
+ private int sampleA = -1;
+ private int sampleB = -1;
+ private int size = 0;
+
+ private int capacity = INITIAL_CAPACITY;
+ private int[] edgesAB1 = new int[INITIAL_CAPACITY];
+ private int[] edgesA2 = new int[INITIAL_CAPACITY];
+ private int[] edgesB2 = new int[INITIAL_CAPACITY];
+ private float[] fwdValues = new float[INITIAL_CAPACITY];
+ private float[] bwdValues = new float[INITIAL_CAPACITY];
+ private float fwdValueSum = 0f;
+ private float bwdValueSum = 0f;
+
+ private int nGenotypes = 0;
+ private float[] gtProbsA = new float[3];
+ private float[] gtProbsB = new float[3];
+
+ /**
+ * Constructs a new {@code DuoBaumLevel} instance from the specified data.
+ * @param dag the directed acyclic graph that the determines transition
+ * probabilities
+ * @param gl the emission probabilities
+ * @throws IllegalArgumentException if
+ * {@code dag.markers().equals(gl.markers()) == false}
+ * @throws NullPointerException if {@code dag == null || gl == null}
+ */
+ public DuoBaumLevel(Dag dag, GL gl) {
+ if (dag.markers().equals(gl.markers())==false) {
+ throw new IllegalArgumentException("marker inconsistency");
+ }
+ this.dag = dag;
+ this.gl = gl;
+ }
+
+ /**
+ * Sets the Baum forward algorithm values for this level of the HMM
+ * and records the child node trio values in the specified
+ * {@code nodes} parameter. When the method call returns, the {@code nodes}
+ * parameter will be reset to the child node trio values for this level of
+ * the HMM.
+ *
+ * @param nodes child node trio values at the previous level of the HMM
+ * @param marker the level of the HMM at which the Baum forward algorithm
+ * probabilities will be computed
+ * @param sampleA the parent sample index
+ * @param sampleB the offspring sample index
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code marker < 0 || marker >= this.dag().nMarkers()}
+ * @throws IndexOutOfBoundsException if
+ * {@code sampleA < 0 || sampleA >= this.gl().nSamples()}
+ * @throws IndexOutOfBoundsException if
+ * {@code sampleB < 0 || sampleB >= this.gl().nSamples()}
+ * @throws IndexOutOfBoundsException if any node in any node trio with
+ * non-zero value is not a valid parent node at the specified level of the
+ * HMM
+ * @throws NullPointerException if {@code nodes == null}
+ */
+ public void setForwardValues(DuoNodes nodes, int marker, int sampleA,
+ int sampleB) {
+ this.marker = marker;
+ this.sampleA = sampleA;
+ this.sampleB = sampleB;
+ this.nGenotypes = gl.marker(marker).nGenotypes();
+ this.size = 0;
+ this.fwdValueSum = 0f;
+ this.bwdValueSum = 0f;
+ initializeGtProbs(); // initialized here due to gtProbs() contract
+ setStates(nodes);
+ setChildNodes(nodes);
+ }
+
+ private void initializeGtProbs() {
+ if (gtProbsA.length < nGenotypes) {
+ int newLength = Math.max(nGenotypes, (3*gtProbsA.length/2 + 1));
+ gtProbsA = new float[newLength];
+ gtProbsB = new float[newLength];
+ }
+ else {
+ for (int j=0; j<nGenotypes; ++j) {
+ gtProbsA[j] = 0f;
+ gtProbsB[j] = 0f;
+ }
+ }
+ }
+
+ private void setStates(DuoNodes nodes) {
+ float valueSum = 0f;
+ for (int j=0, n=nodes.size(); j<n; ++j) {
+ int nodeAB1 = nodes.enumNodeAB1(j);
+ int nodeA2 = nodes.enumNodeA2(j);
+ int nodeB2 = nodes.enumNodeB2(j);
+ float nodeValue = nodes.enumValue(j);
+ for (int ab1=0, nAB1=dag.nOutEdges(marker, nodeAB1); ab1<nAB1; ++ab1) {
+ int edgeAB1 = dag.outEdge(marker, nodeAB1, ab1);
+ int symbolAB1 = dag.symbol(marker, edgeAB1);
+ for (int a2=0, nA2=dag.nOutEdges(marker, nodeA2); a2<nA2; ++a2) {
+ int edgeA2 = dag.outEdge(marker, nodeA2, a2);
+ int symbolA2 = dag.symbol(marker, edgeA2);
+ float epA = gl.gl(marker, sampleA, symbolAB1, symbolA2);
+ if (epA > 0.0) {
+ for (int b2=0, nB2=dag.nOutEdges(marker, nodeB2); b2<nB2; ++b2) {
+ int edgeB2 = dag.outEdge(marker, nodeB2, b2);
+ int symbolB2 = dag.symbol(marker, edgeB2);
+ float epB = gl.gl(marker, sampleB, symbolAB1, symbolB2);
+ if (epB > 0.0) {
+ if (size == capacity) {
+ ensureCapacity(size+1);
+ }
+ float tpAB1 = dag.condEdgeProb(marker, edgeAB1);
+ float tpA2 = dag.condEdgeProb(marker, edgeA2);
+ float tpB2 = dag.condEdgeProb(marker, edgeB2);
+ float fwdValue = (epA * epB) * nodeValue
+ * (tpAB1 * tpA2 * tpB2);
+ if (fwdValue<MIN_VALUE && nodeValue > 0.0) {
+ fwdValue = MIN_VALUE;
+ }
+ edgesAB1[size] = edgeAB1;
+ edgesA2[size] = edgeA2;
+ edgesB2[size] = edgeB2;
+ fwdValues[size++] = fwdValue;
+ valueSum += fwdValue;
+ }
+ }
+ }
+ }
+ }
+ }
+ assert valueSum>0.0 ^ size==0;
+ for (int k=0; k<size; ++k) {
+ this.fwdValues[k] /= valueSum;
+ }
+ fwdValueSum = valueSum;
+ }
+
+ /**
+ * Stores the Baum forward algorithm child node trio values for this
+ * level of the HMM in the specified {@code DuoNodes} object.
+ *
+ * @param nodes the node trio values that will be set
+ *
+ * @throws NullPointerException if {@code nodes == null}
+ */
+ public void setChildNodes(DuoNodes nodes) {
+ nodes.clear();
+ for (int k=0; k<size; ++k) {
+ int nodeAB1 = dag.childNode(marker, edgesAB1[k]);
+ int nodeA2 = dag.childNode(marker, edgesA2[k]);
+ int nodeB2 = dag.childNode(marker, edgesB2[k]);
+ nodes.sumUpdate(nodeAB1, nodeA2, nodeB2, fwdValues[k]);
+ }
+ }
+
+ /**
+ * Sets the Baum backward algorithm values for this level of the HMM
+ * and stores the parent node trio values in the specified
+ * {@code nodes} parameter. When the method call returns, the
+ * ${@code nodes} parameter will be reset to the parent node trio values
+ * for this level of the HMM.
+ *
+ * @param nodes parent node trio values at the next level of HMM
+ *
+ * @throws IndexOutOfBoundsException if any node in any node trio with
+ * non-zero value is not a valid child node at this level of the HMM
+ * @throws NullPointerException if {@code nodes == null}
+ */
+ public void setBackwardValues(DuoNodes nodes) {
+ for (int j=0; j<size; ++j) {
+ int nodeAB1 = dag.childNode(marker, edgesAB1[j]);
+ int nodeA2 = dag.childNode(marker, edgesA2[j]);
+ int nodeB2 = dag.childNode(marker, edgesB2[j]);
+ float backwardValue = nodes.value(nodeAB1, nodeA2, nodeB2);
+ bwdValues[j] = backwardValue;
+ bwdValueSum += backwardValue;
+ }
+ nodes.clear();
+ float gtProbsSum = 0f;
+ for (int j=0; j<size; ++j) {
+ bwdValues[j] /= bwdValueSum;
+ int symbolAB1 = symbolAB1(j);
+ int symbolA2 = symbolA2(j);
+ int symbolB2 = symbolB2(j);
+ float tpAB1 = dag.condEdgeProb(marker, edgesAB1[j]);
+ float tpA2 = dag.condEdgeProb(marker, edgesA2[j]);
+ float tpB2 = dag.condEdgeProb(marker, edgesB2[j]);
+
+ float stateProb = fwdValues[j] * bwdValues[j];
+ int gtIndexA = BasicGL.genotype(symbolAB1, symbolA2);
+ int gtIndexB = BasicGL.genotype(symbolAB1, symbolB2);
+ // gtProbs[AB] assumed to be initialized in setForwardValues() method
+ gtProbsA[gtIndexA] += stateProb;
+ gtProbsB[gtIndexB] += stateProb;
+ gtProbsSum += stateProb;
+
+ float epA = gl.gl(marker, sampleA, symbolAB1, symbolA2);
+ float epB = gl.gl(marker, sampleB, symbolAB1, symbolB2);
+ float bwdValue = bwdValues[j] * (tpAB1 * tpA2 * tpB2) * (epA*epB);
+ if (bwdValue < MIN_VALUE && bwdValues[j] > 0f) {
+ bwdValue = MIN_VALUE;
+ }
+ int pnAB1 = dag.parentNode(marker, edgesAB1[j]);
+ int pnA2 = dag.parentNode(marker, edgesA2[j]);
+ int pnB2 = dag.parentNode(marker, edgesB2[j]);
+ nodes.sumUpdate(pnAB1, pnA2, pnB2, bwdValue);
+ }
+ for (int j=0; j<nGenotypes; ++j) {
+ gtProbsA[j] /= gtProbsSum;
+ gtProbsB[j] /= gtProbsSum;
+ }
+ }
+
+ /**
+ * Returns the directed acyclic graph that determines the transition
+ * probabilities.
+ * @return the directed acyclic graph that determines the transition
+ * probabilities
+ */
+ public Dag dag() {
+ return dag;
+ }
+
+ /**
+ * Returns the emission probabilities.
+ * @return the emission probabilities
+ */
+ public GL gl() {
+ return gl;
+ }
+
+ /**
+ * Return the level of the HMM.
+ * @return the level of the HMM
+ */
+ public int marker() {
+ return marker;
+ }
+
+ /**
+ * Return the number of possible genotypes at this level of the HMM.
+ * @return the number of possible genotypes at this level of the HMM
+ */
+ public int nGenotypes() {
+ return nGenotypes;
+ }
+
+ /**
+ * Returns the specified posterior genotype probability for the parent.
+ * Returns 0 if the Baum backward probabilities have not been set.
+ * @param gt a genotype index
+ * @return the specified posterior genotype probability for the parent
+ * @throws IndexOutOfBoundsException if
+ * {@code gt < 0 || gt >= this.nGenotypes()}
+ */
+ public float gtProbsA(int gt) {
+ checkGT(gt);
+ return gtProbsA[gt];
+ }
+
+ /**
+ * Returns the specified posterior genotype probability for the offspring.
+ * Returns 0 if the Baum backward probabilities have not been set.
+ * @param gt a genotype index
+ * @return the specified posterior genotype probability for the offspring
+ * @throws IndexOutOfBoundsException if
+ * {@code gt < 0 || gt >= this.nGenotypes()}
+ */
+ public float gtProbsB(int gt) {
+ checkGT(gt);
+ return gtProbsB[gt];
+ }
+
+ private void checkGT(int gt) {
+ if (gt >= nGenotypes) {
+ throw new IllegalArgumentException(String.valueOf(gt));
+ }
+ }
+
+ /**
+ * Return the number of states with nonzero forward probability at
+ * this level of the HMM.
+ *
+ * @return the number of states with nonzero forward probability at
+ * this level of the HMM
+ */
+ public int size() {
+ return size;
+ }
+
+ private void checkIndex(int state) {
+ if (state >= size) {
+ throw new IndexOutOfBoundsException(String.valueOf(size));
+ }
+ }
+
+ /**
+ * Returns the first edge of the specified HMM state with nonzero forward
+ * probability.
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the first edge of the specified HMM state with nonzero forward
+ * probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int edgeAB1(int state) {
+ checkIndex(state);
+ return edgesAB1[state];
+ }
+
+ /**
+ * Returns the second edge of the specified HMM state with nonzero forward
+ * probability.
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the second edge of the specified HMM state with nonzero forward
+ * probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int edgeA2(int state) {
+ checkIndex(state);
+ return edgesA2[state];
+ }
+
+ /**
+ * Returns the third edge of the specified HMM state with nonzero forward
+ * probability.
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the third edge of the specified HMM state with nonzero forward
+ * probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int edgeB2(int state) {
+ checkIndex(state);
+ return edgesB2[state];
+ }
+
+ /**
+ * Returns the parent node of the first edge of the specified HMM state
+ * with nonzero forward probability.
+ *
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the parent node of the first edge of the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int parentNodeAB1(int state) {
+ checkIndex(state);
+ return dag.parentNode(marker, edgesAB1[state]);
+ }
+
+ /**
+ * Returns the parent node of the second edge of the specified HMM state
+ * with nonzero forward probability.
+ *
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the parent node of the second edge of the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int parentNodeA2(int state) {
+ checkIndex(state);
+ return dag.parentNode(marker, edgesA2[state]);
+ }
+
+ /**
+ * Returns the parent node of the third edge of the specified HMM state
+ * with nonzero forward probability.
+ *
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the parent node of the third edge of the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int parentNodeB2(int state) {
+ checkIndex(state);
+ return dag.parentNode(marker, edgesB2[state]);
+ }
+
+ /**
+ * Returns the child node of the first edge of the specified HMM state
+ * with nonzero forward probability.
+ *
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the child node of the first edge of the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int childNodeAB1(int state) {
+ checkIndex(state);
+ return dag.childNode(marker, edgesAB1[state]);
+ }
+
+ /**
+ * Returns the child node of the second edge of the specified HMM state
+ * with nonzero forward probability.
+ *
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the child node of the second edge of the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int childNodeA2(int state) {
+ checkIndex(state);
+ return dag.childNode(marker, edgesA2[state]);
+ }
+
+ /**
+ * Returns the child node of the third edge of the specified HMM state
+ * with nonzero forward probability.
+ *
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the child node of the third edge of the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int childNodeB2(int state) {
+ checkIndex(state);
+ return dag.childNode(marker, edgesB2[state]);
+ }
+
+ /**
+ * Returns the symbol for the first edge of the specified HMM state
+ * with nonzero forward probability.
+ *
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the symbol for the first edge of the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int symbolAB1(int state) {
+ return dag.symbol(marker, edgeAB1(state));
+ }
+
+ /**
+ * Returns the symbol for the second edge of the specified HMM state
+ * with nonzero forward probability.
+ *
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the symbol for the second edge of the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int symbolA2(int state) {
+ return dag.symbol(marker, edgeA2(state));
+ }
+
+ /**
+ * Returns the symbol for the third edge of the specified HMM state
+ * with nonzero forward probability.
+ *
+ * @param state an index of a HMM state with nonzero forward probability
+ * @return the symbol for the third edge of the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public int symbolB2(int state) {
+ return dag.symbol(marker, edgeB2(state));
+ }
+
+ /**
+ * Returns the normalized forward value for the specified HMM state
+ * with nonzero forward probability.
+ * The normalized forward value is obtained by dividing the
+ * forward value by the sum of the forward values at this level
+ * of the HMM.
+ *
+ * @param state an index of a HMM state with nonzero forward probability
+ *
+ * @return the normalized forward value for the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public float forwardValue(int state) {
+ checkIndex(state);
+ return fwdValues[state];
+ }
+
+ /**
+ * Returns the normalized backward value for the specified HMM state
+ * with nonzero forward probability.
+ * The normalized backward value is obtained by dividing the
+ * backward value by the sum of the backward values at this level
+ * of the HMM.
+ *
+ * @param state an index of a state with nonzero forward probability
+ *
+ * @return the normalized backward value for the specified HMM state
+ * with nonzero forward probability
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code state < 0 || state >= this.size()}
+ */
+ public float backwardValue(int state) {
+ checkIndex(state);
+ return bwdValues[state];
+ }
+
+ /**
+ * Returns the sum of the forward values at this level of the HMM
+ * when the forward values are computed using forward values
+ * from the previous level that are normalized to sum to 1.
+ * @return the sum of the forward values at this level of the HMM
+ */
+ public float forwardValuesSum() {
+ return fwdValueSum;
+ }
+
+ /**
+ * Returns the sum of the backward values at this level of the HMM
+ * when the backward values are computed using backward
+ * values from the next level that are normalized to sum to 1.
+ * @return the sum of the backward values at this level of the HMM
+ */
+ public float backwardValuesSum() {
+ return bwdValueSum;
+ }
+
+ /**
+ * Returns a string description of {@code this}. The exact details
+ * of the description are unspecified and subject to change.
+ *
+ * @return a string description of {@code this}.
+ */
+ @Override
+ public String toString() {
+ String space = " ";
+ String sep = " | ";
+ StringBuilder sb = new StringBuilder(100);
+ sb.append("level=");
+ sb.append(marker);
+ sb.append(" size=");
+ sb.append(size);
+ sb.append(" forwardValuesSum=");
+ sb.append(fwdValueSum);
+ sb.append(" backwardSum=");
+ sb.append(bwdValueSum);
+ for (int j=0; j<size; ++j) {
+ sb.append(sep);
+ sb.append("j=");
+ sb.append(j);
+ sb.append(": ");
+ sb.append( (int) edgeAB1(j));
+ sb.append(space);
+ sb.append( (int) edgeA2(j));
+ sb.append(space);
+ sb.append( (int) edgeB2(j));
+ sb.append(space);
+ sb.append(forwardValue(j));
+ sb.append(space);
+ sb.append(backwardValue(j));
+ }
+ sb.append(sep);
+ return sb.toString();
+ }
+
+ /*
+ * Increases the state capacity of array fields as necessary
+ * to be greater than or equal to the specified minimum capacity.
+ *
+ * @param minCapacity the desired minimum state capacity
+ */
+ private void ensureCapacity(int minCapacity) {
+ if (minCapacity > capacity) {
+ capacity = (capacity * 3)/2 + 1;
+ if (capacity < minCapacity) {
+ capacity = minCapacity;
+ }
+ edgesAB1 = Arrays.copyOf(edgesAB1, capacity);
+ edgesA2 = Arrays.copyOf(edgesA2, capacity);
+ edgesB2 = Arrays.copyOf(edgesB2, capacity);
+ fwdValues = Arrays.copyOf(fwdValues, capacity);
+ bwdValues = Arrays.copyOf(bwdValues, capacity);
+ }
+ }
+}
diff --git a/sample/DuoNodes.java b/sample/DuoNodes.java
index 907e004..fd5afe5 100644
--- a/sample/DuoNodes.java
+++ b/sample/DuoNodes.java
@@ -1,338 +1,338 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package sample;
-
-/**
- * <p>Class {@code DuoNodes} stores ordered node trios and associated values.
- * </p>
- * <p>Instances of class {@code DuoNodes} are not thread safe.
- * </p>
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class DuoNodes {
-
- private static final float loadFactor = 0.75f;
-
- private int size;
- private int capacity; // required to be a power of 2
- private int rehashThreshold;
-
- private int[] index;
- private int[] nodeAB1;
- private int[] nodeA2;
- private int[] nodeB2;
- private float[] value;
-
- /**
- * Creates a new instance of {@code DuoNodes} that has an
- * initial value of 0 for each ordered node trio.
- */
- public DuoNodes() {
- this.size = 0;
- this.capacity = (1<<10);
- this.rehashThreshold = (int) (loadFactor * capacity);
- this.index = new int[capacity];
- this.nodeAB1 = new int[capacity];
- this.nodeA2 = new int[capacity];
- this.nodeB2 = new int[capacity];
- this.value = new float[capacity];
- }
-
- private static long hash1(int nodeAB1, int nodeA2, int nodeB2) {
- long hash = 5;
- hash = 71 * hash + nodeAB1;
- hash = 71 * hash + nodeA2;
- hash = 71 * hash + nodeB2;
- return hash;
- }
-
- private static long hash2(int nodeAB1, int nodeA2, int nodeB2) {
- long hash = 7;
- hash = 97 * hash + nodeAB1;
- hash = 97 * hash + nodeA2;
- hash = 97 * hash + nodeB2;
- return hash;
- }
-
- /*
- * Return the storage index for specified node trio. If the key is not
- * currently stored in the hash table, the index at which the value
- * should be stored is returned.
- */
- private int index(int ab1, int a2, int b2) {
- long h1 = hash1(ab1, a2, b2);
- long h2 = hash2(ab1, a2, b2);
- if ((h2 & 1)==0) {
- // h2 must be relatively prime to maxSize, which is a power of 2
- ++h2;
- }
- long l = h1;
- for (int k=0; k<capacity; ++k) {
- int i = (int) (l % capacity);
- if (value[i]==0.0 ||
- (nodeAB1[i]==ab1 && nodeA2[i]==a2 && nodeB2[i]==b2)) {
- return i;
- }
- l += h2;
- }
- assert false;
- return -1;
- }
-
- /*
- * Increases the capacity of the internal hash table.
- */
- private void rehash() {
- assert this.size>=this.rehashThreshold;
- int newMaxSize = 2*capacity;
- if (newMaxSize<0) {
- throw new IllegalStateException("hash table overflow");
- }
- int[] oldIndex = index;
- int[] oldNodeAB1 = nodeAB1;
- int[] oldNodeA2 = nodeA2;
- int[] oldNodeB2 = nodeB2;
- float[] oldValue = value;
-
- capacity = newMaxSize;
- index = new int[newMaxSize];
- nodeAB1 = new int[newMaxSize];
- nodeA2 = new int[newMaxSize];
- nodeB2 = new int[newMaxSize];
- value = new float[newMaxSize];
-
- for (int j=0; j<size; ++j) {
- int oldInd = oldIndex[j];
- int newIndex = index(oldNodeAB1[oldInd], oldNodeA2[oldInd],
- oldNodeB2[oldInd]);
- index[j] = newIndex;
- nodeAB1[newIndex] = oldNodeAB1[oldInd];
- nodeA2[newIndex] = oldNodeA2[oldInd];
- nodeB2[newIndex] = oldNodeB2[oldInd];
- value[newIndex] = oldValue[oldInd];
- }
- rehashThreshold = (int) (loadFactor * capacity);
- }
-
- /**
- * Adds the specified value to the stored value of the specified
- * node trio.
- *
- * @param nodeAB1 the first node
- * @param nodeA2 the second node
- * @param nodeB2 the third node
- * @param value the value
- *
- * @throws IllegalArgumentException if
- * {@code (nodeAB1 < 0 || nodeA2 < 0 || nodeB2 < 0)}
- * @throws IllegalArgumentException if
- * {@code value <= 0 || (Double.isFinite(value) == false)}
- */
- public void sumUpdate(int nodeAB1, int nodeA2, int nodeB2, float value) {
- if (nodeAB1 < 0) {
- throw new IllegalArgumentException(String.valueOf(nodeAB1));
- }
- if (nodeA2 < 0) {
- throw new IllegalArgumentException(String.valueOf(nodeA2));
- }
- if (nodeB2 < 0) {
- throw new IllegalArgumentException(String.valueOf(nodeB2));
- }
- if (value <= 0 || (Double.isFinite(value)==false) ) {
- throw new IllegalArgumentException(String.valueOf(value));
- }
- if (value>0.0) {
- int i = index(nodeAB1, nodeA2, nodeB2);
- boolean addNode = (this.value[i]==0f);
- this.value[i] += value;
- if (addNode) {
- this.index[size++] = i;
- this.nodeAB1[i] = nodeAB1;
- this.nodeA2[i] = nodeA2;
- this.nodeB2[i] = nodeB2;
- if (this.size>=this.rehashThreshold) {
- rehash();
- }
- }
- }
- }
-
- /**
- * Returns the number of node trios with non-zero value.
- * @return the number of node trios with non-zero value
- */
- public int size() {
- return size;
- }
-
- private void checkSize(int index) {
- if (index>=size()) {
- throw new IndexOutOfBoundsException(String.valueOf(index));
- }
- }
-
- /**
- * Returns the first node of the specified node trio in a list of
- * node trios with non-zero value. Repeated invocations of this
- * method with the same parameter will return the same value if
- * node values are not modified between invocations. If
- * {@code (index >= 0 && index < this.size())}, then the following
- * expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNodeAB1(index), this.enumNodeA2(index),
- * this.enumNodeB2(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of node trios with non-zero value.
- * @return the first node of the specified node trio in a list of
- * node trios with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public int enumNodeAB1(int index) {
- checkSize(index);
- return nodeAB1[this.index[index]];
- }
-
- /**
- * Returns the second node of the specified node trio in a list of
- * node trios with non-zero value. Repeated invocations of this
- * method with the same parameter will return the same value if
- * node values are not modified between invocations. If
- * {@code (index >= 0 && index < this.size())}, then the following
- * expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNodeAB1(index), this.enumNodeA2(index),
- * this.enumNodeB2(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of node trios with non-zero value
- * @return the second node of the specified node trio in a list of
- * node trios with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public int enumNodeA2(int index) {
- checkSize(index);
- return nodeA2[this.index[index]];
- }
-
- /**
- * Returns the third node of the specified node trio in a list of
- * node trios with non-zero value. Repeated invocations of this
- * method with the same parameter will return the same value if
- * node values are not modified between invocations. If
- * {@code (index >= 0 && index < this.size())}, then the following
- * expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNodeAB1(index), this.enumNodeA2(index),
- * this.enumNodeB2(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of node trios with non-zero value
- * @return the third node of the specified node trio in a list of
- * node trios with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public int enumNodeB2(int index) {
- checkSize(index);
- return nodeB2[this.index[index]];
- }
-
- /**
- * Returns the value of the specified ordered node trio in a list of
- * node trios with non-zero value. Repeated invocations of this
- * method with the same parameter will return the same value if
- * node values are not modified between invocations. If
- * {@code (index >= 0 && index < this.size())}, then the following
- * expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNodeAB1(index), this.enumNodeA2(index),
- * this.enumNodeB2(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of node trios with non-zero value
- * @return the value of the specified ordered node trio in a list of
- * node trios with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public float enumValue(int index) {
- checkSize(index);
- return value[this.index[index]];
- }
-
- /**
- * Returns the specified node trio value.
- *
- * @param nodeAB1 the first node
- * @param nodeA2 the second node
- * @param nodeB2 the third node
- * @return the specified node trio value
- * @throws IllegalArgumentException if
- * {@code (nodeAB1 < 0 || nodeA2 < 0 || nodeB2 < 0)}
- */
- public float value(int nodeAB1, int nodeA2, int nodeB2) {
- if (nodeAB1 < 0) {
- throw new IllegalArgumentException(String.valueOf(nodeAB1));
- }
- if (nodeA2 < 0) {
- throw new IllegalArgumentException(String.valueOf(nodeA2));
- }
- if (nodeB2 < 0) {
- throw new IllegalArgumentException(String.valueOf(nodeB2));
- }
- return value[index(nodeAB1, nodeA2, nodeB2)];
- }
-
- /**
- * Sets the value of each node trio to 0.
- */
- public void clear() {
- for (int j=0; j<this.size; ++j) {
- value[index[j]] = 0f;
- }
- size = 0;
- }
-
- /**
- * Returns a string representation of {@code this}. The exact
- * details of the representation are unspecified and subject to change.
- *
- * @return a string representation of {@code this}.
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(80);
- sb.append("size=");
- sb.append(size);
- for (int j=0; j<size; ++j) {
- sb.append(" (");
- sb.append(j);
- sb.append(": nodeAB1=");
- sb.append(enumNodeAB1(j));
- sb.append(" nodeA2=");
- sb.append((int) enumNodeA2(j));
- sb.append(" nodeB2=");
- sb.append(enumNodeB2(j));
- sb.append(" value=");
- sb.append(enumValue(j));
- sb.append(") ");
- }
- return sb.toString();
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package sample;
+
+/**
+ * <p>Class {@code DuoNodes} stores ordered node trios and associated values.
+ * </p>
+ * <p>Instances of class {@code DuoNodes} are not thread safe.
+ * </p>
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class DuoNodes {
+
+ private static final float loadFactor = 0.75f;
+
+ private int size;
+ private int capacity; // required to be a power of 2
+ private int rehashThreshold;
+
+ private int[] index;
+ private int[] nodeAB1;
+ private int[] nodeA2;
+ private int[] nodeB2;
+ private float[] value;
+
+ /**
+ * Creates a new instance of {@code DuoNodes} that has an
+ * initial value of 0 for each ordered node trio.
+ */
+ public DuoNodes() {
+ this.size = 0;
+ this.capacity = (1<<10);
+ this.rehashThreshold = (int) (loadFactor * capacity);
+ this.index = new int[capacity];
+ this.nodeAB1 = new int[capacity];
+ this.nodeA2 = new int[capacity];
+ this.nodeB2 = new int[capacity];
+ this.value = new float[capacity];
+ }
+
+ private static long hash1(int nodeAB1, int nodeA2, int nodeB2) {
+ long hash = 5;
+ hash = 71 * hash + nodeAB1;
+ hash = 71 * hash + nodeA2;
+ hash = 71 * hash + nodeB2;
+ return hash;
+ }
+
+ private static long hash2(int nodeAB1, int nodeA2, int nodeB2) {
+ long hash = 7;
+ hash = 97 * hash + nodeAB1;
+ hash = 97 * hash + nodeA2;
+ hash = 97 * hash + nodeB2;
+ return hash;
+ }
+
+ /*
+ * Return the storage index for specified node trio. If the key is not
+ * currently stored in the hash table, the index at which the value
+ * should be stored is returned.
+ */
+ private int index(int ab1, int a2, int b2) {
+ long h1 = hash1(ab1, a2, b2);
+ long h2 = hash2(ab1, a2, b2);
+ if ((h2 & 1)==0) {
+ // h2 must be relatively prime to maxSize, which is a power of 2
+ ++h2;
+ }
+ long l = h1;
+ for (int k=0; k<capacity; ++k) {
+ int i = (int) (l % capacity);
+ if (value[i]==0.0 ||
+ (nodeAB1[i]==ab1 && nodeA2[i]==a2 && nodeB2[i]==b2)) {
+ return i;
+ }
+ l += h2;
+ }
+ assert false;
+ return -1;
+ }
+
+ /*
+ * Increases the capacity of the internal hash table.
+ */
+ private void rehash() {
+ assert this.size>=this.rehashThreshold;
+ int newMaxSize = 2*capacity;
+ if (newMaxSize<0) {
+ throw new IllegalStateException("hash table overflow");
+ }
+ int[] oldIndex = index;
+ int[] oldNodeAB1 = nodeAB1;
+ int[] oldNodeA2 = nodeA2;
+ int[] oldNodeB2 = nodeB2;
+ float[] oldValue = value;
+
+ capacity = newMaxSize;
+ index = new int[newMaxSize];
+ nodeAB1 = new int[newMaxSize];
+ nodeA2 = new int[newMaxSize];
+ nodeB2 = new int[newMaxSize];
+ value = new float[newMaxSize];
+
+ for (int j=0; j<size; ++j) {
+ int oldInd = oldIndex[j];
+ int newIndex = index(oldNodeAB1[oldInd], oldNodeA2[oldInd],
+ oldNodeB2[oldInd]);
+ index[j] = newIndex;
+ nodeAB1[newIndex] = oldNodeAB1[oldInd];
+ nodeA2[newIndex] = oldNodeA2[oldInd];
+ nodeB2[newIndex] = oldNodeB2[oldInd];
+ value[newIndex] = oldValue[oldInd];
+ }
+ rehashThreshold = (int) (loadFactor * capacity);
+ }
+
+ /**
+ * Adds the specified value to the stored value of the specified
+ * node trio.
+ *
+ * @param nodeAB1 the first node
+ * @param nodeA2 the second node
+ * @param nodeB2 the third node
+ * @param value the value
+ *
+ * @throws IllegalArgumentException if
+ * {@code (nodeAB1 < 0 || nodeA2 < 0 || nodeB2 < 0)}
+ * @throws IllegalArgumentException if
+ * {@code value <= 0 || (Double.isFinite(value) == false)}
+ */
+ public void sumUpdate(int nodeAB1, int nodeA2, int nodeB2, float value) {
+ if (nodeAB1 < 0) {
+ throw new IllegalArgumentException(String.valueOf(nodeAB1));
+ }
+ if (nodeA2 < 0) {
+ throw new IllegalArgumentException(String.valueOf(nodeA2));
+ }
+ if (nodeB2 < 0) {
+ throw new IllegalArgumentException(String.valueOf(nodeB2));
+ }
+ if (value <= 0 || (Double.isFinite(value)==false) ) {
+ throw new IllegalArgumentException(String.valueOf(value));
+ }
+ if (value>0.0) {
+ int i = index(nodeAB1, nodeA2, nodeB2);
+ boolean addNode = (this.value[i]==0f);
+ this.value[i] += value;
+ if (addNode) {
+ this.index[size++] = i;
+ this.nodeAB1[i] = nodeAB1;
+ this.nodeA2[i] = nodeA2;
+ this.nodeB2[i] = nodeB2;
+ if (this.size>=this.rehashThreshold) {
+ rehash();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the number of node trios with non-zero value.
+ * @return the number of node trios with non-zero value
+ */
+ public int size() {
+ return size;
+ }
+
+ private void checkSize(int index) {
+ if (index>=size()) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ }
+
+ /**
+ * Returns the first node of the specified node trio in a list of
+ * node trios with non-zero value. Repeated invocations of this
+ * method with the same parameter will return the same value if
+ * node values are not modified between invocations. If
+ * {@code (index >= 0 && index < this.size())}, then the following
+ * expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNodeAB1(index), this.enumNodeA2(index),
+ * this.enumNodeB2(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of node trios with non-zero value.
+ * @return the first node of the specified node trio in a list of
+ * node trios with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public int enumNodeAB1(int index) {
+ checkSize(index);
+ return nodeAB1[this.index[index]];
+ }
+
+ /**
+ * Returns the second node of the specified node trio in a list of
+ * node trios with non-zero value. Repeated invocations of this
+ * method with the same parameter will return the same value if
+ * node values are not modified between invocations. If
+ * {@code (index >= 0 && index < this.size())}, then the following
+ * expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNodeAB1(index), this.enumNodeA2(index),
+ * this.enumNodeB2(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of node trios with non-zero value
+ * @return the second node of the specified node trio in a list of
+ * node trios with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public int enumNodeA2(int index) {
+ checkSize(index);
+ return nodeA2[this.index[index]];
+ }
+
+ /**
+ * Returns the third node of the specified node trio in a list of
+ * node trios with non-zero value. Repeated invocations of this
+ * method with the same parameter will return the same value if
+ * node values are not modified between invocations. If
+ * {@code (index >= 0 && index < this.size())}, then the following
+ * expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNodeAB1(index), this.enumNodeA2(index),
+ * this.enumNodeB2(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of node trios with non-zero value
+ * @return the third node of the specified node trio in a list of
+ * node trios with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public int enumNodeB2(int index) {
+ checkSize(index);
+ return nodeB2[this.index[index]];
+ }
+
+ /**
+ * Returns the value of the specified ordered node trio in a list of
+ * node trios with non-zero value. Repeated invocations of this
+ * method with the same parameter will return the same value if
+ * node values are not modified between invocations. If
+ * {@code (index >= 0 && index < this.size())}, then the following
+ * expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNodeAB1(index), this.enumNodeA2(index),
+ * this.enumNodeB2(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of node trios with non-zero value
+ * @return the value of the specified ordered node trio in a list of
+ * node trios with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public float enumValue(int index) {
+ checkSize(index);
+ return value[this.index[index]];
+ }
+
+ /**
+ * Returns the specified node trio value.
+ *
+ * @param nodeAB1 the first node
+ * @param nodeA2 the second node
+ * @param nodeB2 the third node
+ * @return the specified node trio value
+ * @throws IllegalArgumentException if
+ * {@code (nodeAB1 < 0 || nodeA2 < 0 || nodeB2 < 0)}
+ */
+ public float value(int nodeAB1, int nodeA2, int nodeB2) {
+ if (nodeAB1 < 0) {
+ throw new IllegalArgumentException(String.valueOf(nodeAB1));
+ }
+ if (nodeA2 < 0) {
+ throw new IllegalArgumentException(String.valueOf(nodeA2));
+ }
+ if (nodeB2 < 0) {
+ throw new IllegalArgumentException(String.valueOf(nodeB2));
+ }
+ return value[index(nodeAB1, nodeA2, nodeB2)];
+ }
+
+ /**
+ * Sets the value of each node trio to 0.
+ */
+ public void clear() {
+ for (int j=0; j<this.size; ++j) {
+ value[index[j]] = 0f;
+ }
+ size = 0;
+ }
+
+ /**
+ * Returns a string representation of {@code this}. The exact
+ * details of the representation are unspecified and subject to change.
+ *
+ * @return a string representation of {@code this}.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(80);
+ sb.append("size=");
+ sb.append(size);
+ for (int j=0; j<size; ++j) {
+ sb.append(" (");
+ sb.append(j);
+ sb.append(": nodeAB1=");
+ sb.append(enumNodeAB1(j));
+ sb.append(" nodeA2=");
+ sb.append((int) enumNodeA2(j));
+ sb.append(" nodeB2=");
+ sb.append(enumNodeB2(j));
+ sb.append(" value=");
+ sb.append(enumValue(j));
+ sb.append(") ");
+ }
+ return sb.toString();
+ }
+}
diff --git a/sample/HapNodes.java b/sample/HapNodes.java
index 805bb83..6d99d82 100644
--- a/sample/HapNodes.java
+++ b/sample/HapNodes.java
@@ -1,233 +1,233 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package sample;
-
-/**
- * <p>Class {@code HapNodes} stores nodes and associated values.
- * </p>
- * <p>Instances of class {@code HapNodes} are not thread safe.</p>
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class HapNodes {
-
- private static final float loadFactor = 0.75f;
-
- private int size;
- private int capacity; // required to be a power of 2
- private int rehashThreshold;
-
- private int[] index;
- private int[] node;
- private float[] value;
-
- /**
- * Creates a new instance of {@code HapNodes} that has an
- * initial value of 0 for each node.
- */
- public HapNodes() {
- this.size = 0;
- this.capacity = (1<<10);
- this.rehashThreshold = (int) (loadFactor * capacity);
- this.index = new int[capacity];
- this.node = new int[capacity];
- this.value = new float[capacity];
- }
-
- /*
- * Return the storage index for specified node. If the key is not
- * currently stored in the hash table, the index at which the value
- * should be stored is returned.
- */
- private int index(int node) {
- long l = (71 * 5) + node;
- long h2 = (97 * 7) + node;
- if ((h2 & 1)==0) {
- // h2 must be relatively prime to maxSize, which is a power of 2
- ++h2;
- }
- for (int k=0; k<capacity; ++k) {
- int i = (int) (l % capacity);
- if (value[i]==0.0 || (this.node[i]==node)) {
- return i;
- }
- l += h2;
- }
- assert false;
- return -1;
- }
-
- /*
- * Increases the capacity of the internal hash table.
- */
- private void rehash() {
- assert this.size>=this.rehashThreshold;
- int newMaxSize = 2*capacity;
- if (newMaxSize<0) {
- throw new IllegalStateException("hash table overflow");
- }
- int[] oldIndex = index;
- int[] oldNode = node;
- float[] oldValue = value;
-
- capacity = newMaxSize;
- index = new int[newMaxSize];
- node = new int[newMaxSize];
- value = new float[newMaxSize];
-
- for (int j=0; j<size; ++j) {
- int oldInd = oldIndex[j];
- int newIndex = index(oldNode[oldInd]);
- index[j] = newIndex;
- node[newIndex] = oldNode[oldInd];
- value[newIndex] = oldValue[oldInd];
- }
- rehashThreshold = (int) (loadFactor * capacity);
- }
-
- /**
- * Adds the specified value to the stored value of the specified
- * node.
- *
- * @param node the node
- * @param value the value
- *
- * @throws IllegalArgumentException if {@code node < 0}
- * @throws IllegalArgumentException if
- * {@code value <= 0 || (Double.isFinite(value) == false)}
- */
- public void sumUpdate(int node, float value) {
- if (node < 0) {
- throw new IllegalArgumentException(String.valueOf(node));
- }
- if (value <= 0 || (Double.isFinite(value)==false) ) {
- throw new IllegalArgumentException(String.valueOf(value));
- }
- int i = index(node);
- boolean addNode = (this.value[i]==0f);
- this.value[i] += value;
- if (addNode) {
- this.index[size++] = i;
- this.node[i] = node;
- if (this.size>=this.rehashThreshold) {
- rehash();
- }
- }
- }
-
- /**
- * Returns the number of nodes with non-zero value.
- * @return the number of nodes with non-zero value
- */
- public int size() {
- return size;
- }
-
- private void checkSize(int index) {
- if (index>=size()) {
- throw new IndexOutOfBoundsException(String.valueOf(index));
- }
- }
-
- /**
- * Returns the specified node in a list of nodes with non-zero value.
- * Repeated invocations of this method with the same parameter will
- * return the same value if node values are not modified between
- * invocations. If {@code (index >= 0 && index < this.size())}, then the
- * following expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNode(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of nodes with non-zero value
- * @return the specified node in the list of nodes with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public int enumNode(int index) {
- checkSize(index);
- return node[this.index[index]];
- }
-
- /**
- * Returns the value of the specified node in a list of nodes with
- * non-zero value. Repeated invocations of this method with the same
- * parameter will return the same value if node values are not modified
- * between invocations. If {@code (index >= 0 && index < this.size())}, then
- * the following expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNode(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of nodes with non-zero value
- * @return the value of the specified node in a list of nodes with
- * non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public float enumValue(int index) {
- checkSize(index);
- return value[this.index[index]];
- }
-
- /**
- * Returns the specified node value.
- *
- * @param node the first node
- * @return the specified node value
- * @throws IllegalArgumentException if {@code node < 0}
- */
- public float value(int node) {
- if (node < 0) {
- throw new IllegalArgumentException(String.valueOf(node));
- }
- return value[index(node)];
- }
-
- /**
- * Sets the value of each node to 0.
- */
- public void clear() {
- for (int j=0; j<this.size; ++j) {
- value[index[j]] = 0f;
- }
- size = 0;
- }
-
- /**
- * Returns a string representation of {@code this}. The exact
- * details of the representation are unspecified and subject to change.
- *
- * @return a string representation of {@code this}
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(80);
- sb.append("size=");
- sb.append(size);
- for (int j=0; j<size; ++j) {
- sb.append(" (");
- sb.append(j);
- sb.append(": node=");
- sb.append(enumNode(j));
- sb.append(" value=");
- sb.append(enumValue(j));
- sb.append(") ");
- }
- return sb.toString();
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package sample;
+
+/**
+ * <p>Class {@code HapNodes} stores nodes and associated values.
+ * </p>
+ * <p>Instances of class {@code HapNodes} are not thread safe.</p>
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class HapNodes {
+
+ private static final float loadFactor = 0.75f;
+
+ private int size;
+ private int capacity; // required to be a power of 2
+ private int rehashThreshold;
+
+ private int[] index;
+ private int[] node;
+ private float[] value;
+
+ /**
+ * Creates a new instance of {@code HapNodes} that has an
+ * initial value of 0 for each node.
+ */
+ public HapNodes() {
+ this.size = 0;
+ this.capacity = (1<<10);
+ this.rehashThreshold = (int) (loadFactor * capacity);
+ this.index = new int[capacity];
+ this.node = new int[capacity];
+ this.value = new float[capacity];
+ }
+
+ /*
+ * Return the storage index for specified node. If the key is not
+ * currently stored in the hash table, the index at which the value
+ * should be stored is returned.
+ */
+ private int index(int node) {
+ long l = (71 * 5) + node;
+ long h2 = (97 * 7) + node;
+ if ((h2 & 1)==0) {
+ // h2 must be relatively prime to maxSize, which is a power of 2
+ ++h2;
+ }
+ for (int k=0; k<capacity; ++k) {
+ int i = (int) (l % capacity);
+ if (value[i]==0.0 || (this.node[i]==node)) {
+ return i;
+ }
+ l += h2;
+ }
+ assert false;
+ return -1;
+ }
+
+ /*
+ * Increases the capacity of the internal hash table.
+ */
+ private void rehash() {
+ assert this.size>=this.rehashThreshold;
+ int newMaxSize = 2*capacity;
+ if (newMaxSize<0) {
+ throw new IllegalStateException("hash table overflow");
+ }
+ int[] oldIndex = index;
+ int[] oldNode = node;
+ float[] oldValue = value;
+
+ capacity = newMaxSize;
+ index = new int[newMaxSize];
+ node = new int[newMaxSize];
+ value = new float[newMaxSize];
+
+ for (int j=0; j<size; ++j) {
+ int oldInd = oldIndex[j];
+ int newIndex = index(oldNode[oldInd]);
+ index[j] = newIndex;
+ node[newIndex] = oldNode[oldInd];
+ value[newIndex] = oldValue[oldInd];
+ }
+ rehashThreshold = (int) (loadFactor * capacity);
+ }
+
+ /**
+ * Adds the specified value to the stored value of the specified
+ * node.
+ *
+ * @param node the node
+ * @param value the value
+ *
+ * @throws IllegalArgumentException if {@code node < 0}
+ * @throws IllegalArgumentException if
+ * {@code value <= 0 || (Double.isFinite(value) == false)}
+ */
+ public void sumUpdate(int node, float value) {
+ if (node < 0) {
+ throw new IllegalArgumentException(String.valueOf(node));
+ }
+ if (value <= 0 || (Double.isFinite(value)==false) ) {
+ throw new IllegalArgumentException(String.valueOf(value));
+ }
+ int i = index(node);
+ boolean addNode = (this.value[i]==0f);
+ this.value[i] += value;
+ if (addNode) {
+ this.index[size++] = i;
+ this.node[i] = node;
+ if (this.size>=this.rehashThreshold) {
+ rehash();
+ }
+ }
+ }
+
+ /**
+ * Returns the number of nodes with non-zero value.
+ * @return the number of nodes with non-zero value
+ */
+ public int size() {
+ return size;
+ }
+
+ private void checkSize(int index) {
+ if (index>=size()) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ }
+
+ /**
+ * Returns the specified node in a list of nodes with non-zero value.
+ * Repeated invocations of this method with the same parameter will
+ * return the same value if node values are not modified between
+ * invocations. If {@code (index >= 0 && index < this.size())}, then the
+ * following expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNode(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of nodes with non-zero value
+ * @return the specified node in the list of nodes with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public int enumNode(int index) {
+ checkSize(index);
+ return node[this.index[index]];
+ }
+
+ /**
+ * Returns the value of the specified node in a list of nodes with
+ * non-zero value. Repeated invocations of this method with the same
+ * parameter will return the same value if node values are not modified
+ * between invocations. If {@code (index >= 0 && index < this.size())}, then
+ * the following expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNode(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of nodes with non-zero value
+ * @return the value of the specified node in a list of nodes with
+ * non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public float enumValue(int index) {
+ checkSize(index);
+ return value[this.index[index]];
+ }
+
+ /**
+ * Returns the specified node value.
+ *
+ * @param node the first node
+ * @return the specified node value
+ * @throws IllegalArgumentException if {@code node < 0}
+ */
+ public float value(int node) {
+ if (node < 0) {
+ throw new IllegalArgumentException(String.valueOf(node));
+ }
+ return value[index(node)];
+ }
+
+ /**
+ * Sets the value of each node to 0.
+ */
+ public void clear() {
+ for (int j=0; j<this.size; ++j) {
+ value[index[j]] = 0f;
+ }
+ size = 0;
+ }
+
+ /**
+ * Returns a string representation of {@code this}. The exact
+ * details of the representation are unspecified and subject to change.
+ *
+ * @return a string representation of {@code this}
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(80);
+ sb.append("size=");
+ sb.append(size);
+ for (int j=0; j<size; ++j) {
+ sb.append(" (");
+ sb.append(j);
+ sb.append(": node=");
+ sb.append(enumNode(j));
+ sb.append(" value=");
+ sb.append(enumValue(j));
+ sb.append(") ");
+ }
+ return sb.toString();
+ }
+}
diff --git a/sample/RecombSingleBaumLevel.java b/sample/RecombSingleBaumLevel.java
index d8ba8ad..2392bfa 100644
--- a/sample/RecombSingleBaumLevel.java
+++ b/sample/RecombSingleBaumLevel.java
@@ -255,9 +255,11 @@ public class RecombSingleBaumLevel {
float nextBaseBwdValue = nextBaseBwdValue(edges1[j], edges2[j],
bwdValues[j]);
- int pn1 = dag.parentNode(marker, edges1[j]);
- int pn2 = dag.parentNode(marker, edges2[j]);
- nodes.sumUpdate(pn1, pn2, nextBaseBwdValue);
+ if (nextBaseBwdValue>0f) {
+ int pn1 = dag.parentNode(marker, edges1[j]);
+ int pn2 = dag.parentNode(marker, edges2[j]);
+ nodes.sumUpdate(pn1, pn2, nextBaseBwdValue);
+ }
}
for (int j=0; j<nGenotypes; ++j) {
gtProbs[j] /= gtProbsSum;
diff --git a/sample/RecombSingleNodes.java b/sample/RecombSingleNodes.java
index 45dfa58..741ef55 100644
--- a/sample/RecombSingleNodes.java
+++ b/sample/RecombSingleNodes.java
@@ -1,361 +1,361 @@
-/*
- * Copyright (C) 2015 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package sample;
-
-/**
- * <p>Class {@code RecombSingleNodes} stores ordered node pairs and
- * associated values.
- * </p>
- * <p>Instances of class {@code RecombSingleNodes} are not thread safe.
- * </p>
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class RecombSingleNodes {
-
- private static final double loadFactor = 0.75;
-
- private final int nNodes;
-
- private int size = 0;
- private int capacity; // required to be a power of 2
- private int rehashThreshold;
-
- private int[] index;
- private int[] node1;
- private int[] node2;
- private float[] value;
- private float[] sumNode1Value;
- private float[] sumNode2Value;
- private float sumValue;
-
- /**
- * Creates a new instance of {@code RecombSingleNodes} that has an
- * initial value of 0 for each ordered node pair. The first node
- * has index 0.
- * @param nNodes the maximum number of distinct nodes
- * which will be paired to form ordered node pairs
- * @throws IllegalArgumentException if {@code nNodes < 1}
- */
- public RecombSingleNodes(int nNodes) {
- if (nNodes < 1) {
- throw new IllegalArgumentException("nNodes < 1: " + nNodes);
- }
- this.nNodes = nNodes;
- this.size = 0;
- this.capacity = (1<<10);
- this.rehashThreshold = (int) (loadFactor * capacity);
- this.index = new int[capacity];
- this.node1 = new int[capacity];
- this.node2 = new int[capacity];
- this.value = new float[capacity];
- this.sumNode1Value = new float[nNodes];
- this.sumNode2Value = new float[nNodes];
- this.sumValue = 0.0f;
- }
-
- private static long hash1(int node1, int node2) {
- long hash = 5;
- hash = 71 * hash + node1;
- hash = 71 * hash + node2;
- return hash;
- }
-
- private static long hash2(int node1, int node2) {
- long hash = 7;
- hash = 97 * hash + node1;
- hash = 97 * hash + node2;
- return hash;
- }
- /*
- * Return the storage index for specified node pair. If the key is not
- * currently stored in the hash table, the index at which the value
- * should be stored is returned.
- */
- private int index(int node1, int node2) {
- long h1 = hash1(node1, node2);
- long h2 = hash2(node1, node2);
- if ((h2 & 1)==0) {
- // h2 must be relatively prime to maxSize, which is a power of 2
- ++h2;
- }
- long l = h1;
- for (int k=0; k<capacity; ++k) {
- int i = (int) (l % capacity);
- if (value[i]==0.0
- || (this.node1[i]==node1 && this.node2[i]==node2)) {
- return i;
- }
- l += h2;
- }
- assert false;
- return -1;
- }
-
- /*
- * Increases the capacity of the internal hash table.
- */
- private void rehash() {
- assert this.size>=this.rehashThreshold;
- int newMaxSize = 2*capacity;
- if (newMaxSize<0) {
- throw new IllegalStateException("hash table overflow");
- }
- int[] oldIndex = index;
- int[] oldNode1 = node1;
- int[] oldNode2 = node2;
- float[] oldValue = value;
-
- capacity = newMaxSize;
- index = new int[newMaxSize];
- node1 = new int[newMaxSize];
- node2 = new int[newMaxSize];
- value = new float[newMaxSize];
-
- for (int j=0; j<size; ++j) {
- int oldInd = oldIndex[j];
- int newIndex = index(oldNode1[oldInd], oldNode2[oldInd]);
- index[j] = newIndex;
- node1[newIndex] = oldNode1[oldInd];
- node2[newIndex] = oldNode2[oldInd];
- value[newIndex] = oldValue[oldInd];
- }
- rehashThreshold = (int) (loadFactor * capacity);
- }
-
- /**
- * Adds the specified positive value to the stored value of the specified
- * node pair.
- *
- * @param node1 the first node
- * @param node2 the second node
- * @param value the value
- *
- * @throws IndexOutOfBoundsException if
- * {@code node1 < 0 || node1 >= this.nNodes()}
- * @throws IndexOutOfBoundsException if
- * {@code node2 < 0 || node2 >= this.nNodes()}
- * @throws IllegalArgumentException if
- * {@code value <= 0 || (Double.isFinite(value) == false)}
- */
- public void sumUpdate(int node1, int node2, float value) {
- if (value <= 0 || (Double.isFinite(value)==false) ) {
- throw new IllegalArgumentException(String.valueOf(value));
- }
- int i = index(node1, node2);
- boolean addNode = (this.value[i]==0f);
- this.value[i] += value;
- this.sumNode1Value[node1] += value;
- this.sumNode2Value[node2] += value;
- this.sumValue += value;
- if (addNode) {
- this.index[size++] = i;
- this.node1[i] = node1;
- this.node2[i] = node2;
- if (this.size>=this.rehashThreshold) {
- rehash();
- }
- }
- }
-
- /**
- * Returns the number of node pairs with non-zero value.
- * @return the number of node pairs with non-zero value
- */
- public int size() {
- return size;
- }
-
- private void checkSize(int index) {
- if (index>=size()) {
- throw new IndexOutOfBoundsException(String.valueOf(index));
- }
- }
-
- /**
- * Returns the number of nodes.
- *
- * @return the number of nodes
- */
- public int nNodes() {
- return nNodes;
- }
-
- /**
- * Returns the first node of the specified node pair in the list of
- * node pairs with non-zero value. Repeated invocations of this
- * method with the same parameter will return the same value if
- * node values are not modified between invocations. If
- * {@code (index >= 0 && index < this.size())}, then the following
- * expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNode1(index),
- * this.enumNode2(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of node pairs with non-zero
- * value
- * @return the first node of the specified node pair in a list of
- * node pairs with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public int enumNode1(int index) {
- checkSize(index);
- return node1[this.index[index]];
- }
-
- /**
- * Returns the second node of the specified node pair in a list of
- * node pairs with non-zero value. Repeated invocations of this
- * method with the same parameter will return the same value if
- * node values are not modified between invocations. If
- * {@code (index >= 0 && index < this.size())}, then the following
- * expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNode1(index),
- * this.enumNode2(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of node pairs with non-zero value
- * @return the second node of the specified node pair in a list of
- * node pairs with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public int enumNode2(int index) {
- checkSize(index);
- return node2[this.index[index]];
- }
-
- /**
- * Returns the value of the specified node pair in a list of
- * node pairs with non-zero value. Repeated invocations of this
- * method with the same parameter will return the same value if
- * node values are not modified between invocations. If
- * {@code (index >= 0 && index < this.size())}, then the following
- * expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNode1(index),
- * this.enumNode2(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of node pairs with non-zero value
- * @return the value of the specified ordered node pair in a list of
- * node pairs with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public float enumValue(int index) {
- checkSize(index);
- return value[this.index[index]];
- }
-
- /**
- * Returns the value of the specified node pair.
- *
- * @param node1 the first node
- * @param node2 the second node
- * @return the value of the specified node pair
- * @throws IllegalArgumentException if {@code node1 < 0 || node2 < 0}
- */
- public float value(int node1, int node2) {
- if (node1 < 0 || node1 >= nNodes) {
- throw new IndexOutOfBoundsException(String.valueOf(node1));
- }
- if (node2 < 0 || node2 >= nNodes) {
- throw new IndexOutOfBoundsException(String.valueOf(node2));
- }
- return value[index(node1, node2)];
- }
-
- /**
- * Returns the sum of the values of the node pairs that have the specified
- * first node
- *
- * @param node1 a node
- * @return the sum of the values of the node pairs that have the specified
- * first node
- *
- * @throws IndexOutOfBoundsException if
- * {@code node1 < 0 || node1 >= this.nNodes()}
- */
- public float sumNode1Value(int node1) {
- return sumNode1Value[node1];
- }
-
- /**
- * Returns the sum of the values of the node pairs that have the specified
- * second node.
- *
- * @param node2 a node
- * @return the sum of the values of the node pairs that have the specified
- * second node
- *
- * @throws IndexOutOfBoundsException if
- * {@code node2 < 0 || node2 >= this.nNodes()}
- */
- public float sumNode2Value(int node2) {
- return sumNode2Value[node2];
- }
-
- /**
- * Returns the sum of the values of all node pairs.
- *
- * @return the sum of the values of all node pairs
- */
- public float sumValue() {
- return sumValue;
- }
-
- /**
- * Sets the value of each ordered node pair to 0.
- */
- public void clear() {
- for (int j=0; j<this.size; ++j) {
- value[index[j]] = 0f;
- sumNode1Value[node1[index[j]]]=0f;
- sumNode2Value[node2[index[j]]]=0f;
- }
- sumValue = 0.0f;
- size = 0;
- }
-
- /**
- * Returns a string representation of {@code this}. The exact
- * details of the representation are unspecified and subject to change.
- *
- * @return a string representation of {@code this}
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(80);
- sb.append("size=");
- sb.append(size);
- for (int j=0; j<size; ++j) {
- sb.append(" (");
- sb.append(j);
- sb.append(": node1=");
- sb.append(enumNode1(j));
- sb.append(" node2=");
- sb.append(enumNode2(j));
- sb.append(" value=");
- sb.append(enumValue(j));
- sb.append(")");
- }
- return sb.toString();
- }
-}
+/*
+ * Copyright (C) 2015 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package sample;
+
+/**
+ * <p>Class {@code RecombSingleNodes} stores ordered node pairs and
+ * associated values.
+ * </p>
+ * <p>Instances of class {@code RecombSingleNodes} are not thread safe.
+ * </p>
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class RecombSingleNodes {
+
+ private static final double loadFactor = 0.75;
+
+ private final int nNodes;
+
+ private int size = 0;
+ private int capacity; // required to be a power of 2
+ private int rehashThreshold;
+
+ private int[] index;
+ private int[] node1;
+ private int[] node2;
+ private float[] value;
+ private float[] sumNode1Value;
+ private float[] sumNode2Value;
+ private float sumValue;
+
+ /**
+ * Creates a new instance of {@code RecombSingleNodes} that has an
+ * initial value of 0 for each ordered node pair. The first node
+ * has index 0.
+ * @param nNodes the maximum number of distinct nodes
+ * which will be paired to form ordered node pairs
+ * @throws IllegalArgumentException if {@code nNodes < 1}
+ */
+ public RecombSingleNodes(int nNodes) {
+ if (nNodes < 1) {
+ throw new IllegalArgumentException("nNodes < 1: " + nNodes);
+ }
+ this.nNodes = nNodes;
+ this.size = 0;
+ this.capacity = (1<<10);
+ this.rehashThreshold = (int) (loadFactor * capacity);
+ this.index = new int[capacity];
+ this.node1 = new int[capacity];
+ this.node2 = new int[capacity];
+ this.value = new float[capacity];
+ this.sumNode1Value = new float[nNodes];
+ this.sumNode2Value = new float[nNodes];
+ this.sumValue = 0.0f;
+ }
+
+ private static long hash1(int node1, int node2) {
+ long hash = 5;
+ hash = 71 * hash + node1;
+ hash = 71 * hash + node2;
+ return hash;
+ }
+
+ private static long hash2(int node1, int node2) {
+ long hash = 7;
+ hash = 97 * hash + node1;
+ hash = 97 * hash + node2;
+ return hash;
+ }
+ /*
+ * Return the storage index for specified node pair. If the key is not
+ * currently stored in the hash table, the index at which the value
+ * should be stored is returned.
+ */
+ private int index(int node1, int node2) {
+ long h1 = hash1(node1, node2);
+ long h2 = hash2(node1, node2);
+ if ((h2 & 1)==0) {
+ // h2 must be relatively prime to maxSize, which is a power of 2
+ ++h2;
+ }
+ long l = h1;
+ for (int k=0; k<capacity; ++k) {
+ int i = (int) (l % capacity);
+ if (value[i]==0.0
+ || (this.node1[i]==node1 && this.node2[i]==node2)) {
+ return i;
+ }
+ l += h2;
+ }
+ assert false;
+ return -1;
+ }
+
+ /*
+ * Increases the capacity of the internal hash table.
+ */
+ private void rehash() {
+ assert this.size>=this.rehashThreshold;
+ int newMaxSize = 2*capacity;
+ if (newMaxSize<0) {
+ throw new IllegalStateException("hash table overflow");
+ }
+ int[] oldIndex = index;
+ int[] oldNode1 = node1;
+ int[] oldNode2 = node2;
+ float[] oldValue = value;
+
+ capacity = newMaxSize;
+ index = new int[newMaxSize];
+ node1 = new int[newMaxSize];
+ node2 = new int[newMaxSize];
+ value = new float[newMaxSize];
+
+ for (int j=0; j<size; ++j) {
+ int oldInd = oldIndex[j];
+ int newIndex = index(oldNode1[oldInd], oldNode2[oldInd]);
+ index[j] = newIndex;
+ node1[newIndex] = oldNode1[oldInd];
+ node2[newIndex] = oldNode2[oldInd];
+ value[newIndex] = oldValue[oldInd];
+ }
+ rehashThreshold = (int) (loadFactor * capacity);
+ }
+
+ /**
+ * Adds the specified positive value to the stored value of the specified
+ * node pair.
+ *
+ * @param node1 the first node
+ * @param node2 the second node
+ * @param value the value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code node1 < 0 || node1 >= this.nNodes()}
+ * @throws IndexOutOfBoundsException if
+ * {@code node2 < 0 || node2 >= this.nNodes()}
+ * @throws IllegalArgumentException if
+ * {@code value <= 0 || (Double.isFinite(value) == false)}
+ */
+ public void sumUpdate(int node1, int node2, float value) {
+ if (value <= 0 || (Double.isFinite(value)==false) ) {
+ throw new IllegalArgumentException(String.valueOf(value));
+ }
+ int i = index(node1, node2);
+ boolean addNode = (this.value[i]==0f);
+ this.value[i] += value;
+ this.sumNode1Value[node1] += value;
+ this.sumNode2Value[node2] += value;
+ this.sumValue += value;
+ if (addNode) {
+ this.index[size++] = i;
+ this.node1[i] = node1;
+ this.node2[i] = node2;
+ if (this.size>=this.rehashThreshold) {
+ rehash();
+ }
+ }
+ }
+
+ /**
+ * Returns the number of node pairs with non-zero value.
+ * @return the number of node pairs with non-zero value
+ */
+ public int size() {
+ return size;
+ }
+
+ private void checkSize(int index) {
+ if (index>=size()) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ }
+
+ /**
+ * Returns the number of nodes.
+ *
+ * @return the number of nodes
+ */
+ public int nNodes() {
+ return nNodes;
+ }
+
+ /**
+ * Returns the first node of the specified node pair in the list of
+ * node pairs with non-zero value. Repeated invocations of this
+ * method with the same parameter will return the same value if
+ * node values are not modified between invocations. If
+ * {@code (index >= 0 && index < this.size())}, then the following
+ * expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNode1(index),
+ * this.enumNode2(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of node pairs with non-zero
+ * value
+ * @return the first node of the specified node pair in a list of
+ * node pairs with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public int enumNode1(int index) {
+ checkSize(index);
+ return node1[this.index[index]];
+ }
+
+ /**
+ * Returns the second node of the specified node pair in a list of
+ * node pairs with non-zero value. Repeated invocations of this
+ * method with the same parameter will return the same value if
+ * node values are not modified between invocations. If
+ * {@code (index >= 0 && index < this.size())}, then the following
+ * expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNode1(index),
+ * this.enumNode2(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of node pairs with non-zero value
+ * @return the second node of the specified node pair in a list of
+ * node pairs with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public int enumNode2(int index) {
+ checkSize(index);
+ return node2[this.index[index]];
+ }
+
+ /**
+ * Returns the value of the specified node pair in a list of
+ * node pairs with non-zero value. Repeated invocations of this
+ * method with the same parameter will return the same value if
+ * node values are not modified between invocations. If
+ * {@code (index >= 0 && index < this.size())}, then the following
+ * expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNode1(index),
+ * this.enumNode2(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of node pairs with non-zero value
+ * @return the value of the specified ordered node pair in a list of
+ * node pairs with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public float enumValue(int index) {
+ checkSize(index);
+ return value[this.index[index]];
+ }
+
+ /**
+ * Returns the value of the specified node pair.
+ *
+ * @param node1 the first node
+ * @param node2 the second node
+ * @return the value of the specified node pair
+ * @throws IllegalArgumentException if {@code node1 < 0 || node2 < 0}
+ */
+ public float value(int node1, int node2) {
+ if (node1 < 0 || node1 >= nNodes) {
+ throw new IndexOutOfBoundsException(String.valueOf(node1));
+ }
+ if (node2 < 0 || node2 >= nNodes) {
+ throw new IndexOutOfBoundsException(String.valueOf(node2));
+ }
+ return value[index(node1, node2)];
+ }
+
+ /**
+ * Returns the sum of the values of the node pairs that have the specified
+ * first node
+ *
+ * @param node1 a node
+ * @return the sum of the values of the node pairs that have the specified
+ * first node
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code node1 < 0 || node1 >= this.nNodes()}
+ */
+ public float sumNode1Value(int node1) {
+ return sumNode1Value[node1];
+ }
+
+ /**
+ * Returns the sum of the values of the node pairs that have the specified
+ * second node.
+ *
+ * @param node2 a node
+ * @return the sum of the values of the node pairs that have the specified
+ * second node
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code node2 < 0 || node2 >= this.nNodes()}
+ */
+ public float sumNode2Value(int node2) {
+ return sumNode2Value[node2];
+ }
+
+ /**
+ * Returns the sum of the values of all node pairs.
+ *
+ * @return the sum of the values of all node pairs
+ */
+ public float sumValue() {
+ return sumValue;
+ }
+
+ /**
+ * Sets the value of each ordered node pair to 0.
+ */
+ public void clear() {
+ for (int j=0; j<this.size; ++j) {
+ value[index[j]] = 0f;
+ sumNode1Value[node1[index[j]]]=0f;
+ sumNode2Value[node2[index[j]]]=0f;
+ }
+ sumValue = 0.0f;
+ size = 0;
+ }
+
+ /**
+ * Returns a string representation of {@code this}. The exact
+ * details of the representation are unspecified and subject to change.
+ *
+ * @return a string representation of {@code this}
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(80);
+ sb.append("size=");
+ sb.append(size);
+ for (int j=0; j<size; ++j) {
+ sb.append(" (");
+ sb.append(j);
+ sb.append(": node1=");
+ sb.append(enumNode1(j));
+ sb.append(" node2=");
+ sb.append(enumNode2(j));
+ sb.append(" value=");
+ sb.append(enumValue(j));
+ sb.append(")");
+ }
+ return sb.toString();
+ }
+}
diff --git a/sample/SingleBaumLevel.java b/sample/SingleBaumLevel.java
index ee75939..af90296 100644
--- a/sample/SingleBaumLevel.java
+++ b/sample/SingleBaumLevel.java
@@ -214,9 +214,11 @@ public class SingleBaumLevel {
if (bwdValue < MIN_VALUE && bwdValues[j]>0.0) {
bwdValue = MIN_VALUE;
}
- int pn1 = dag.parentNode(marker, edge1);
- int pn2 = dag.parentNode(marker, edge2);
- nodes.sumUpdate(pn1, pn2, bwdValue);
+ if (bwdValue>0f) {
+ int pn1 = dag.parentNode(marker, edge1);
+ int pn2 = dag.parentNode(marker, edge2);
+ nodes.sumUpdate(pn1, pn2, bwdValue);
+ }
}
for (int j=0; j<nGenotypes; ++j) {
gtProbs[j] /= gtProbsSum;
diff --git a/sample/SingleNodes.java b/sample/SingleNodes.java
index 0801493..18651c7 100644
--- a/sample/SingleNodes.java
+++ b/sample/SingleNodes.java
@@ -1,294 +1,294 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package sample;
-
-/**
- * <p>Class {@code SingleNodes} stores ordered node pairs and associated values.
- * </p>
- * <p>Instances of class {@code SingleNodes} are not thread safe.</p>
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class SingleNodes {
-
- private static final float loadFactor = 0.75f;
-
- private int size;
- private int capacity; // required to be a power of 2
- private int rehashThreshold;
-
- private int[] index;
- private int[] node1;
- private int[] node2;
- private float[] value;
-
- /**
- * Creates a new instance of {@code SingleNodes} that has an
- * initial value of 0 for each ordered node pair. The first node
- * has index 0.
- */
- public SingleNodes() {
- this.size = 0;
- this.capacity = (1<<10);
- this.rehashThreshold = (int) (loadFactor * capacity);
- this.index = new int[capacity];
- this.node1 = new int[capacity];
- this.node2 = new int[capacity];
- this.value = new float[capacity];
- }
-
- private static long hash1(int node1, int node2) {
- long hash = 5;
- hash = 71 * hash + node1;
- hash = 71 * hash + node2;
- return hash;
- }
-
- private static long hash2(int node1, int node2) {
- long hash = 7;
- hash = 97 * hash + node1;
- hash = 97 * hash + node2;
- return hash;
- }
-
- /*
- * Return the storage index for specified node pair. If the key is not
- * currently stored in the hash table, the index at which the value
- * should be stored is returned.
- */
- private int index(int node1, int node2) {
- long h1 = hash1(node1, node2);
- long h2 = hash2(node1, node2);
- if ((h2 & 1)==0) {
- // h2 must be relatively prime to maxSize, which is a power of 2
- ++h2;
- }
- long l = h1;
- for (int k=0; k<capacity; ++k) {
- int i = (int) (l % capacity);
- if (value[i]==0.0
- || (this.node1[i]==node1 && this.node2[i]==node2)) {
- return i;
- }
- l += h2;
- }
- assert false;
- return -1;
- }
-
- /*
- * Increases the capacity of the internal hash table.
- */
- private void rehash() {
- assert this.size>=this.rehashThreshold;
- int newMaxSize = 2*capacity;
- if (newMaxSize<0) {
- throw new IllegalStateException("hash table overflow");
- }
- int[] oldIndex = index;
- int[] oldNode1 = node1;
- int[] oldNode2 = node2;
- float[] oldValue = value;
-
- capacity = newMaxSize;
- index = new int[newMaxSize];
- node1 = new int[newMaxSize];
- node2 = new int[newMaxSize];
- value = new float[newMaxSize];
-
- for (int j=0; j<size; ++j) {
- int oldInd = oldIndex[j];
- int newIndex = index(oldNode1[oldInd], oldNode2[oldInd]);
- index[j] = newIndex;
- node1[newIndex] = oldNode1[oldInd];
- node2[newIndex] = oldNode2[oldInd];
- value[newIndex] = oldValue[oldInd];
- }
- rehashThreshold = (int) (loadFactor * capacity);
- }
-
- /**
- * Adds the specified positive value to the stored value of the specified
- * node pair.
- *
- * @param node1 the first node
- * @param node2 the second node
- * @param value the value
- *
- * @throws IllegalArgumentException if {@code node1 < 0 || node2 < 0}
- * @throws IllegalArgumentException if
- * {@code value <= 0 || (Double.isFinite(value) == false)}
- */
- public void sumUpdate(int node1, int node2, float value) {
- if (node1 < 0) {
- throw new IllegalArgumentException(String.valueOf(node1));
- }
- if (node2 < 0) {
- throw new IllegalArgumentException(String.valueOf(node2));
- }
- if (value <= 0 || (Double.isFinite(value)==false) ) {
- throw new IllegalArgumentException(String.valueOf(value));
- }
- int i = index(node1, node2);
- boolean addNode = (this.value[i]==0f);
- this.value[i] += value;
- if (addNode) {
- this.index[size++] = i;
- this.node1[i] = node1;
- this.node2[i] = node2;
- if (this.size>=this.rehashThreshold) {
- rehash();
- }
- }
- }
-
- /**
- * Returns the number of node pairs with non-zero value.
- * @return the number of node pairs with non-zero value
- */
- public int size() {
- return size;
- }
-
- private void checkSize(int index) {
- if (index>=size()) {
- throw new IndexOutOfBoundsException(String.valueOf(index));
- }
- }
-
- /**
- * Returns the first node of the specified node pair in the list of
- * node pairs with non-zero value. Repeated invocations of this
- * method with the same parameter will return the same value if
- * node values are not modified between invocations. If
- * {@code (index >= 0 && index < this.size())}, then the following
- * expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNode1(index),
- * this.enumNode2(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of node pairs with non-zero
- * value
- * @return the first node of the specified node pair in a list of
- * node pairs with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public int enumNode1(int index) {
- checkSize(index);
- return node1[this.index[index]];
- }
-
- /**
- * Returns the second node of the specified node pair in a list of
- * node pairs with non-zero value. Repeated invocations of this
- * method with the same parameter will return the same value if
- * node values are not modified between invocations. If
- * {@code (index >= 0 && index < this.size())}, then the following
- * expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNode1(index),
- * this.enumNode2(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of node pairs with non-zero value
- * @return the second node of the specified node pair in a list of
- * node pairs with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public int enumNode2(int index) {
- checkSize(index);
- return node2[this.index[index]];
- }
-
- /**
- * Returns the value of the specified node pair in a list of
- * node pairs with non-zero value. Repeated invocations of this
- * method with the same parameter will return the same value if
- * node values are not modified between invocations. If
- * {@code (index >= 0 && index < this.size())}, then the following
- * expression will always evaluate to {@code true}:<br>
- * {@code (this.value(this.enumNode1(index),
- * this.enumNode2(index)) == this.enumValue(index))}.
- *
- * @param index an index in a list of node pairs with non-zero value
- * @return the value of the specified ordered node pair in a list of
- * node pairs with non-zero value
- *
- * @throws IndexOutOfBoundsException if
- * {@code index < 0 || index >= this.size()}
- */
- public float enumValue(int index) {
- checkSize(index);
- return value[this.index[index]];
- }
-
- /**
- * Returns the value of the specified node pair.
- *
- * @param node1 the first node
- * @param node2 the second node
- * @return the value of the specified node pair
- * @throws IllegalArgumentException if {@code node1 < 0 || node2 < 0}
- */
- public float value(int node1, int node2) {
- if (node1 < 0) {
- throw new IllegalArgumentException(String.valueOf(node1));
- }
- if (node2 < 0) {
- throw new IllegalArgumentException(String.valueOf(node2));
- }
- return value[index(node1, node2)];
- }
-
- /**
- * Sets the value of each ordered node pair to 0.
- */
- public void clear() {
- for (int j=0; j<this.size; ++j) {
- value[index[j]] = 0f;
- }
- size = 0;
- }
-
- /**
- * Returns a string representation of {@code this}. The exact
- * details of the representation are unspecified and subject to change.
- *
- * @return a string representation of {@code this}
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(80);
- sb.append("size=");
- sb.append(size);
- for (int j=0; j<size; ++j) {
- sb.append(" (");
- sb.append(j);
- sb.append(": node1=");
- sb.append(enumNode1(j));
- sb.append(" node2=");
- sb.append(enumNode2(j));
- sb.append(" value=");
- sb.append(enumValue(j));
- sb.append(") ");
- }
- return sb.toString();
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package sample;
+
+/**
+ * <p>Class {@code SingleNodes} stores ordered node pairs and associated values.
+ * </p>
+ * <p>Instances of class {@code SingleNodes} are not thread safe.</p>
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class SingleNodes {
+
+ private static final float loadFactor = 0.75f;
+
+ private int size;
+ private int capacity; // required to be a power of 2
+ private int rehashThreshold;
+
+ private int[] index;
+ private int[] node1;
+ private int[] node2;
+ private float[] value;
+
+ /**
+ * Creates a new instance of {@code SingleNodes} that has an
+ * initial value of 0 for each ordered node pair. The first node
+ * has index 0.
+ */
+ public SingleNodes() {
+ this.size = 0;
+ this.capacity = (1<<10);
+ this.rehashThreshold = (int) (loadFactor * capacity);
+ this.index = new int[capacity];
+ this.node1 = new int[capacity];
+ this.node2 = new int[capacity];
+ this.value = new float[capacity];
+ }
+
+ private static long hash1(int node1, int node2) {
+ long hash = 5;
+ hash = 71 * hash + node1;
+ hash = 71 * hash + node2;
+ return hash;
+ }
+
+ private static long hash2(int node1, int node2) {
+ long hash = 7;
+ hash = 97 * hash + node1;
+ hash = 97 * hash + node2;
+ return hash;
+ }
+
+ /*
+ * Return the storage index for specified node pair. If the key is not
+ * currently stored in the hash table, the index at which the value
+ * should be stored is returned.
+ */
+ private int index(int node1, int node2) {
+ long h1 = hash1(node1, node2);
+ long h2 = hash2(node1, node2);
+ if ((h2 & 1)==0) {
+ // h2 must be relatively prime to maxSize, which is a power of 2
+ ++h2;
+ }
+ long l = h1;
+ for (int k=0; k<capacity; ++k) {
+ int i = (int) (l % capacity);
+ if (value[i]==0.0
+ || (this.node1[i]==node1 && this.node2[i]==node2)) {
+ return i;
+ }
+ l += h2;
+ }
+ assert false;
+ return -1;
+ }
+
+ /*
+ * Increases the capacity of the internal hash table.
+ */
+ private void rehash() {
+ assert this.size>=this.rehashThreshold;
+ int newMaxSize = 2*capacity;
+ if (newMaxSize<0) {
+ throw new IllegalStateException("hash table overflow");
+ }
+ int[] oldIndex = index;
+ int[] oldNode1 = node1;
+ int[] oldNode2 = node2;
+ float[] oldValue = value;
+
+ capacity = newMaxSize;
+ index = new int[newMaxSize];
+ node1 = new int[newMaxSize];
+ node2 = new int[newMaxSize];
+ value = new float[newMaxSize];
+
+ for (int j=0; j<size; ++j) {
+ int oldInd = oldIndex[j];
+ int newIndex = index(oldNode1[oldInd], oldNode2[oldInd]);
+ index[j] = newIndex;
+ node1[newIndex] = oldNode1[oldInd];
+ node2[newIndex] = oldNode2[oldInd];
+ value[newIndex] = oldValue[oldInd];
+ }
+ rehashThreshold = (int) (loadFactor * capacity);
+ }
+
+ /**
+ * Adds the specified positive value to the stored value of the specified
+ * node pair.
+ *
+ * @param node1 the first node
+ * @param node2 the second node
+ * @param value the value
+ *
+ * @throws IllegalArgumentException if {@code node1 < 0 || node2 < 0}
+ * @throws IllegalArgumentException if
+ * {@code value <= 0 || (Double.isFinite(value) == false)}
+ */
+ public void sumUpdate(int node1, int node2, float value) {
+ if (node1 < 0) {
+ throw new IllegalArgumentException(String.valueOf(node1));
+ }
+ if (node2 < 0) {
+ throw new IllegalArgumentException(String.valueOf(node2));
+ }
+ if (value <= 0 || (Double.isFinite(value)==false) ) {
+ throw new IllegalArgumentException(String.valueOf(value));
+ }
+ int i = index(node1, node2);
+ boolean addNode = (this.value[i]==0f);
+ this.value[i] += value;
+ if (addNode) {
+ this.index[size++] = i;
+ this.node1[i] = node1;
+ this.node2[i] = node2;
+ if (this.size>=this.rehashThreshold) {
+ rehash();
+ }
+ }
+ }
+
+ /**
+ * Returns the number of node pairs with non-zero value.
+ * @return the number of node pairs with non-zero value
+ */
+ public int size() {
+ return size;
+ }
+
+ private void checkSize(int index) {
+ if (index>=size()) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ }
+
+ /**
+ * Returns the first node of the specified node pair in the list of
+ * node pairs with non-zero value. Repeated invocations of this
+ * method with the same parameter will return the same value if
+ * node values are not modified between invocations. If
+ * {@code (index >= 0 && index < this.size())}, then the following
+ * expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNode1(index),
+ * this.enumNode2(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of node pairs with non-zero
+ * value
+ * @return the first node of the specified node pair in a list of
+ * node pairs with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public int enumNode1(int index) {
+ checkSize(index);
+ return node1[this.index[index]];
+ }
+
+ /**
+ * Returns the second node of the specified node pair in a list of
+ * node pairs with non-zero value. Repeated invocations of this
+ * method with the same parameter will return the same value if
+ * node values are not modified between invocations. If
+ * {@code (index >= 0 && index < this.size())}, then the following
+ * expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNode1(index),
+ * this.enumNode2(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of node pairs with non-zero value
+ * @return the second node of the specified node pair in a list of
+ * node pairs with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public int enumNode2(int index) {
+ checkSize(index);
+ return node2[this.index[index]];
+ }
+
+ /**
+ * Returns the value of the specified node pair in a list of
+ * node pairs with non-zero value. Repeated invocations of this
+ * method with the same parameter will return the same value if
+ * node values are not modified between invocations. If
+ * {@code (index >= 0 && index < this.size())}, then the following
+ * expression will always evaluate to {@code true}:<br>
+ * {@code (this.value(this.enumNode1(index),
+ * this.enumNode2(index)) == this.enumValue(index))}.
+ *
+ * @param index an index in a list of node pairs with non-zero value
+ * @return the value of the specified ordered node pair in a list of
+ * node pairs with non-zero value
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code index < 0 || index >= this.size()}
+ */
+ public float enumValue(int index) {
+ checkSize(index);
+ return value[this.index[index]];
+ }
+
+ /**
+ * Returns the value of the specified node pair.
+ *
+ * @param node1 the first node
+ * @param node2 the second node
+ * @return the value of the specified node pair
+ * @throws IllegalArgumentException if {@code node1 < 0 || node2 < 0}
+ */
+ public float value(int node1, int node2) {
+ if (node1 < 0) {
+ throw new IllegalArgumentException(String.valueOf(node1));
+ }
+ if (node2 < 0) {
+ throw new IllegalArgumentException(String.valueOf(node2));
+ }
+ return value[index(node1, node2)];
+ }
+
+ /**
+ * Sets the value of each ordered node pair to 0.
+ */
+ public void clear() {
+ for (int j=0; j<this.size; ++j) {
+ value[index[j]] = 0f;
+ }
+ size = 0;
+ }
+
+ /**
+ * Returns a string representation of {@code this}. The exact
+ * details of the representation are unspecified and subject to change.
+ *
+ * @return a string representation of {@code this}
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(80);
+ sb.append("size=");
+ sb.append(size);
+ for (int j=0; j<size; ++j) {
+ sb.append(" (");
+ sb.append(j);
+ sb.append(": node1=");
+ sb.append(enumNode1(j));
+ sb.append(" node2=");
+ sb.append(enumNode2(j));
+ sb.append(" value=");
+ sb.append(enumValue(j));
+ sb.append(") ");
+ }
+ return sb.toString();
+ }
+}
diff --git a/vcf/AllData.java b/vcf/AllData.java
index be5f132..bf190b2 100644
--- a/vcf/AllData.java
+++ b/vcf/AllData.java
@@ -1,355 +1,355 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package vcf;
-
-import beagleutil.SampleIds;
-import beagleutil.Samples;
-import blbutil.SampleFileIt;
-import haplotype.HapPair;
-import haplotype.RefHapPairs;
-import haplotype.SampleHapPairs;
-import haplotype.WrappedHapPair;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * <p>Class {@code AllData} represents a sliding window of
- * reference and target VCF records.
- * </p>
- * <p>Instances of class {@code AllData} are not thread-safe.
- * </p>
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class AllData implements Data {
-
- private int window = 0;
- private VcfEmission[] refData;
- private SampleHapPairs refSampleHapPairs;
- private GL refEmissions;
- private VcfEmission[] targetData; // missing markers as null entries
- private int[] refIndices;
- private int[] targetIndices;
- private GL targetEmissions;
- private final Samples allSamples;
-
- private final List<HapPair> refHapPairs;
- private final List<HapPair> targetRefHapPairs; // at target markers
- private final VcfWindow refWindow;
- private final RestrictedVcfWindow targetWindow;
-
- /**
- * Constructs and returns a new {@code AllData} instance from VCF records
- * returned by the specified {@code SampleFileIt} objects.
- *
- * @param refIt an iterator that returns reference VCF records
- * @param targetIt an iterator that returns target VCF records
- * @return a new {@code AllData} instance.
- *
- * @throws IllegalArgumentException if either the reference data or
- * target data contain no samples
- * @throws IllegalArgumentException if a format error is detected
- * in a string VCF record
- * @throws NullPointerException if {@code refIt == null || targetIt == null}
- */
- public static AllData allData(SampleFileIt<VcfEmission> refIt,
- SampleFileIt<? extends VcfEmission> targetIt) {
- if (refIt.samples().nSamples()==0 && targetIt.samples().nSamples()==0) {
- throw new IllegalArgumentException("nSamples==0");
- }
- VcfWindow refWindow = new VcfWindow(refIt);
- RestrictedVcfWindow targetWindow = new RestrictedVcfWindow(targetIt);
- return new AllData(refWindow, targetWindow);
- }
-
- private AllData(VcfWindow refWind, RestrictedVcfWindow targetWind) {
- checkSampleOverlap(refWind.samples(), targetWind.samples());
- this.refWindow = refWind;
- this.targetWindow = targetWind;
-
- this.refData = new VcfEmission[0];
- this.refSampleHapPairs = null;
- this.refEmissions = new RefGL(refWind.samples(), refData);
- this.targetData = new VcfEmission[0];
- this.refIndices = new int[0];
- this.targetIndices = new int[0];
- this.targetEmissions = new BasicGL(targetWind.samples(), targetData);
- this.allSamples = allSamples(refWind.samples(), targetWind.samples());
-
- this.refHapPairs = new ArrayList<>(0);
- this.targetRefHapPairs = new ArrayList<>(0);
- }
-
- private static Samples allSamples(Samples ref, Samples target) {
- /*
- Target samples are listed first so that sample indices agree
- with sample indices in target data genotype likelihoods.
- */
- int nRef = ref.nSamples();
- int nTarget = target.nSamples();
- int[] idIndices = new int[nRef + nTarget];
- for (int j=0; j<nTarget; ++j) {
- idIndices[j] = target.idIndex(j);
- }
- for (int j=0; j<nRef; ++j) {
- idIndices[nTarget + j] = ref.idIndex(j);
- }
- return new Samples(idIndices);
- }
-
- private static void checkSampleOverlap(Samples ref, Samples nonRef) {
- int nRef = ref.nSamples();
- int nNonRef = nonRef.nSamples();
- int n = nRef + nNonRef;
- int[] idIndices = new int[n];
- for (int j=0; j<nRef; ++j) {
- idIndices[j] = ref.idIndex(j);
- }
- for (int j=0; j<nNonRef; ++j) {
- idIndices[nRef + j] = nonRef.idIndex(j);
- }
- Arrays.sort(idIndices);
- for (int j=1; j<idIndices.length; ++j) {
- if (idIndices[j-1]==idIndices[j]) {
- String s = "Overlap between reference and non-reference samples: "
- + SampleIds.instance().id(idIndices[j-1]);
- throw new IllegalArgumentException(s);
- }
- }
- }
-
- @Override
- public boolean lastWindowOnChrom() {
- return refWindow.lastWindowOnChrom();
- }
-
- @Override
- public boolean canAdvanceWindow() {
- return refWindow.canAdvanceWindow();
- }
-
- @Override
- public void advanceWindow(int requestedOverlap, int windowSize) {
- Samples refSamples = refWindow.samples();
- refData = refWindow.advanceWindow(requestedOverlap, windowSize);
- refEmissions = new RefGL(refSamples, refData);
- refSampleHapPairs = new RefHapPairs(refEmissions.markers(), refSamples, refData);
- targetData = targetWindow.advanceWindow(refEmissions.markers());
- refIndices = refIndices(targetData);
- targetIndices = targetIndices(targetData);
- targetEmissions = targetEmissions(targetWindow.samples(),
- targetData, refIndices);
- ++window;
- setRefHaplotypes(refEmissions.markers(), refData);
- setTargetRefHaplotypes(targetEmissions.markers(), refData, refIndices);
- }
-
- @Override
- public int window() {
- return window;
- }
-
- private static int[] refIndices(VcfEmission[] vma) {
- int nonNullCnt = 0;
- for (VcfEmission vm : vma) {
- if (vm!=null) {
- ++nonNullCnt;
- }
- }
- int[] inclusionMap = new int[nonNullCnt];
- int index = 0;
- for (int j=0; j<vma.length; ++j) {
- if (vma[j]!=null) {
- inclusionMap[index++] = j;
- }
- }
- if (index != inclusionMap.length) {
- throw new IllegalStateException("vma modification detected");
- }
- return inclusionMap;
- }
-
- private static int[] targetIndices(VcfEmission[] vma) {
- int[] inclusionMap = new int[vma.length];
- int index = 0;
- for (int j=0; j<inclusionMap.length; ++j) {
- if (vma[j]!=null) {
- inclusionMap[j] = index++;
- }
- else {
- inclusionMap[j] = -1;
- }
- }
- return inclusionMap;
- }
-
- private static GL targetEmissions(Samples samples,
- VcfEmission[] vma, int[] refMarkerIndex) {
- VcfEmission[] restricted = new VcfEmission[refMarkerIndex.length];
- for (int j=0; j<refMarkerIndex.length; ++j) {
- restricted[j] = vma[refMarkerIndex[j]];
- }
- return new BasicGL(samples, restricted);
- }
-
- private void setRefHaplotypes(Markers refMarkers, VcfEmission[] refData) {
- refHapPairs.clear();
- SampleHapPairs refHaplotypes =
- new RefHapPairs(refMarkers, refWindow.samples(), refData);
- for (int j=0, n=refHaplotypes.nSamples(); j<n; ++j) {
- refHapPairs.add(new WrappedHapPair(refHaplotypes, j));
- }
- }
-
- private void setTargetRefHaplotypes(Markers targetMarkers, VcfEmission[] refData,
- int[] refMarkerIndices) {
- assert targetMarkers.nMarkers()==refMarkerIndices.length;
- targetRefHapPairs.clear();
- VcfEmission[] vma = new VcfEmission[refMarkerIndices.length];
- for (int j=0; j<refMarkerIndices.length; ++j) {
- vma[j] = refData[refMarkerIndices[j]];
- }
- SampleHapPairs refHaplotypes
- = new RefHapPairs(targetMarkers, refWindow.samples(), vma);
- for (int j=0, n=refHaplotypes.nSamples(); j<n; ++j) {
- targetRefHapPairs.add(new WrappedHapPair(refHaplotypes, j));
- }
- }
-
- @Override
- public int targetOverlap() {
- return targetWindow.overlap();
- }
-
- @Override
- public int overlap() {
- return refWindow.overlap();
- }
-
- @Override
- public int nTargetMarkers() {
- return targetEmissions.markers().nMarkers();
- }
-
- @Override
- public int nTargetMarkersSoFar() {
- return targetWindow.cumMarkerCnt();
- }
-
- @Override
- public Markers targetMarkers() {
- return targetEmissions.markers();
- }
-
-
- @Override
- public int nMarkers() {
- return refEmissions.nMarkers();
- }
-
- @Override
- public int nMarkersSoFar() {
- return refWindow.cumMarkerCnt();
- }
-
- @Override
- public Markers markers() {
- return refEmissions.markers();
- }
-
- @Override
- public int targetMarkerIndex(int refIndex) {
- return targetIndices[refIndex];
- }
-
- @Override
- public int markerIndex(int nonRefIndex) {
- return refIndices[nonRefIndex];
- }
-
- @Override
- public int nTargetSamples() {
- return targetEmissions.nSamples();
- }
-
- @Override
- public Samples targetSamples() {
- return targetEmissions.samples();
- }
-
- @Override
- public int nRefSamples() {
- return refWindow.nSamples();
- }
-
- @Override
- public Samples refSamples() {
- return refWindow.samples();
- }
-
- @Override
- public int nAllSamples() {
- return allSamples.nSamples();
- }
-
- @Override
- public Samples allSamples() {
- return allSamples;
- }
-
-
- @Override
- public GL targetGL() {
- return targetEmissions;
- }
-
- @Override
- public List<HapPair> restrictedRefHapPairs() {
- return new ArrayList<>(targetRefHapPairs);
- }
-
- @Override
- public List<HapPair> refHapPairs() {
- return new ArrayList<>(refHapPairs);
- }
-
- @Override
- public SampleHapPairs refSampleHapPairs() {
- return refSampleHapPairs;
- }
-
- @Override
- public void close() {
- refWindow.close();
- targetWindow.close();
- }
-
- /**
- * Returns a string representation of {@code this}. The exact
- * details of the representation are unspecified and subject to change.
- * @return a string representation of {@code this}
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(20);
- sb.append(this.getClass().toString());
- return sb.toString();
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package vcf;
+
+import beagleutil.SampleIds;
+import beagleutil.Samples;
+import blbutil.SampleFileIt;
+import haplotype.HapPair;
+import haplotype.RefHapPairs;
+import haplotype.SampleHapPairs;
+import haplotype.WrappedHapPair;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * <p>Class {@code AllData} represents a sliding window of
+ * reference and target VCF records.
+ * </p>
+ * <p>Instances of class {@code AllData} are not thread-safe.
+ * </p>
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class AllData implements Data {
+
+ private int window = 0;
+ private VcfEmission[] refData;
+ private SampleHapPairs refSampleHapPairs;
+ private GL refEmissions;
+ private VcfEmission[] targetData; // missing markers as null entries
+ private int[] refIndices;
+ private int[] targetIndices;
+ private GL targetEmissions;
+ private final Samples allSamples;
+
+ private final List<HapPair> refHapPairs;
+ private final List<HapPair> targetRefHapPairs; // at target markers
+ private final VcfWindow refWindow;
+ private final RestrictedVcfWindow targetWindow;
+
+ /**
+ * Constructs and returns a new {@code AllData} instance from VCF records
+ * returned by the specified {@code SampleFileIt} objects.
+ *
+ * @param refIt an iterator that returns reference VCF records
+ * @param targetIt an iterator that returns target VCF records
+ * @return a new {@code AllData} instance.
+ *
+ * @throws IllegalArgumentException if either the reference data or
+ * target data contain no samples
+ * @throws IllegalArgumentException if a format error is detected
+ * in a string VCF record
+ * @throws NullPointerException if {@code refIt == null || targetIt == null}
+ */
+ public static AllData allData(SampleFileIt<VcfEmission> refIt,
+ SampleFileIt<? extends VcfEmission> targetIt) {
+ if (refIt.samples().nSamples()==0 && targetIt.samples().nSamples()==0) {
+ throw new IllegalArgumentException("nSamples==0");
+ }
+ VcfWindow refWindow = new VcfWindow(refIt);
+ RestrictedVcfWindow targetWindow = new RestrictedVcfWindow(targetIt);
+ return new AllData(refWindow, targetWindow);
+ }
+
+ private AllData(VcfWindow refWind, RestrictedVcfWindow targetWind) {
+ checkSampleOverlap(refWind.samples(), targetWind.samples());
+ this.refWindow = refWind;
+ this.targetWindow = targetWind;
+
+ this.refData = new VcfEmission[0];
+ this.refSampleHapPairs = null;
+ this.refEmissions = new RefGL(refWind.samples(), refData);
+ this.targetData = new VcfEmission[0];
+ this.refIndices = new int[0];
+ this.targetIndices = new int[0];
+ this.targetEmissions = new BasicGL(targetWind.samples(), targetData);
+ this.allSamples = allSamples(refWind.samples(), targetWind.samples());
+
+ this.refHapPairs = new ArrayList<>(0);
+ this.targetRefHapPairs = new ArrayList<>(0);
+ }
+
+ private static Samples allSamples(Samples ref, Samples target) {
+ /*
+ Target samples are listed first so that sample indices agree
+ with sample indices in target data genotype likelihoods.
+ */
+ int nRef = ref.nSamples();
+ int nTarget = target.nSamples();
+ int[] idIndices = new int[nRef + nTarget];
+ for (int j=0; j<nTarget; ++j) {
+ idIndices[j] = target.idIndex(j);
+ }
+ for (int j=0; j<nRef; ++j) {
+ idIndices[nTarget + j] = ref.idIndex(j);
+ }
+ return new Samples(idIndices);
+ }
+
+ private static void checkSampleOverlap(Samples ref, Samples nonRef) {
+ int nRef = ref.nSamples();
+ int nNonRef = nonRef.nSamples();
+ int n = nRef + nNonRef;
+ int[] idIndices = new int[n];
+ for (int j=0; j<nRef; ++j) {
+ idIndices[j] = ref.idIndex(j);
+ }
+ for (int j=0; j<nNonRef; ++j) {
+ idIndices[nRef + j] = nonRef.idIndex(j);
+ }
+ Arrays.sort(idIndices);
+ for (int j=1; j<idIndices.length; ++j) {
+ if (idIndices[j-1]==idIndices[j]) {
+ String s = "Overlap between reference and non-reference samples: "
+ + SampleIds.instance().id(idIndices[j-1]);
+ throw new IllegalArgumentException(s);
+ }
+ }
+ }
+
+ @Override
+ public boolean lastWindowOnChrom() {
+ return refWindow.lastWindowOnChrom();
+ }
+
+ @Override
+ public boolean canAdvanceWindow() {
+ return refWindow.canAdvanceWindow();
+ }
+
+ @Override
+ public void advanceWindow(int requestedOverlap, int windowSize) {
+ Samples refSamples = refWindow.samples();
+ refData = refWindow.advanceWindow(requestedOverlap, windowSize);
+ refEmissions = new RefGL(refSamples, refData);
+ refSampleHapPairs = new RefHapPairs(refEmissions.markers(), refSamples, refData);
+ targetData = targetWindow.advanceWindow(refEmissions.markers());
+ refIndices = refIndices(targetData);
+ targetIndices = targetIndices(targetData);
+ targetEmissions = targetEmissions(targetWindow.samples(),
+ targetData, refIndices);
+ ++window;
+ setRefHaplotypes(refEmissions.markers(), refData);
+ setTargetRefHaplotypes(targetEmissions.markers(), refData, refIndices);
+ }
+
+ @Override
+ public int window() {
+ return window;
+ }
+
+ private static int[] refIndices(VcfEmission[] vma) {
+ int nonNullCnt = 0;
+ for (VcfEmission vm : vma) {
+ if (vm!=null) {
+ ++nonNullCnt;
+ }
+ }
+ int[] inclusionMap = new int[nonNullCnt];
+ int index = 0;
+ for (int j=0; j<vma.length; ++j) {
+ if (vma[j]!=null) {
+ inclusionMap[index++] = j;
+ }
+ }
+ if (index != inclusionMap.length) {
+ throw new IllegalStateException("vma modification detected");
+ }
+ return inclusionMap;
+ }
+
+ private static int[] targetIndices(VcfEmission[] vma) {
+ int[] inclusionMap = new int[vma.length];
+ int index = 0;
+ for (int j=0; j<inclusionMap.length; ++j) {
+ if (vma[j]!=null) {
+ inclusionMap[j] = index++;
+ }
+ else {
+ inclusionMap[j] = -1;
+ }
+ }
+ return inclusionMap;
+ }
+
+ private static GL targetEmissions(Samples samples,
+ VcfEmission[] vma, int[] refMarkerIndex) {
+ VcfEmission[] restricted = new VcfEmission[refMarkerIndex.length];
+ for (int j=0; j<refMarkerIndex.length; ++j) {
+ restricted[j] = vma[refMarkerIndex[j]];
+ }
+ return new BasicGL(samples, restricted);
+ }
+
+ private void setRefHaplotypes(Markers refMarkers, VcfEmission[] refData) {
+ refHapPairs.clear();
+ SampleHapPairs refHaplotypes =
+ new RefHapPairs(refMarkers, refWindow.samples(), refData);
+ for (int j=0, n=refHaplotypes.nSamples(); j<n; ++j) {
+ refHapPairs.add(new WrappedHapPair(refHaplotypes, j));
+ }
+ }
+
+ private void setTargetRefHaplotypes(Markers targetMarkers, VcfEmission[] refData,
+ int[] refMarkerIndices) {
+ assert targetMarkers.nMarkers()==refMarkerIndices.length;
+ targetRefHapPairs.clear();
+ VcfEmission[] vma = new VcfEmission[refMarkerIndices.length];
+ for (int j=0; j<refMarkerIndices.length; ++j) {
+ vma[j] = refData[refMarkerIndices[j]];
+ }
+ SampleHapPairs refHaplotypes
+ = new RefHapPairs(targetMarkers, refWindow.samples(), vma);
+ for (int j=0, n=refHaplotypes.nSamples(); j<n; ++j) {
+ targetRefHapPairs.add(new WrappedHapPair(refHaplotypes, j));
+ }
+ }
+
+ @Override
+ public int targetOverlap() {
+ return targetWindow.overlap();
+ }
+
+ @Override
+ public int overlap() {
+ return refWindow.overlap();
+ }
+
+ @Override
+ public int nTargetMarkers() {
+ return targetEmissions.markers().nMarkers();
+ }
+
+ @Override
+ public int nTargetMarkersSoFar() {
+ return targetWindow.cumMarkerCnt();
+ }
+
+ @Override
+ public Markers targetMarkers() {
+ return targetEmissions.markers();
+ }
+
+
+ @Override
+ public int nMarkers() {
+ return refEmissions.nMarkers();
+ }
+
+ @Override
+ public int nMarkersSoFar() {
+ return refWindow.cumMarkerCnt();
+ }
+
+ @Override
+ public Markers markers() {
+ return refEmissions.markers();
+ }
+
+ @Override
+ public int targetMarkerIndex(int refIndex) {
+ return targetIndices[refIndex];
+ }
+
+ @Override
+ public int markerIndex(int nonRefIndex) {
+ return refIndices[nonRefIndex];
+ }
+
+ @Override
+ public int nTargetSamples() {
+ return targetEmissions.nSamples();
+ }
+
+ @Override
+ public Samples targetSamples() {
+ return targetEmissions.samples();
+ }
+
+ @Override
+ public int nRefSamples() {
+ return refWindow.nSamples();
+ }
+
+ @Override
+ public Samples refSamples() {
+ return refWindow.samples();
+ }
+
+ @Override
+ public int nAllSamples() {
+ return allSamples.nSamples();
+ }
+
+ @Override
+ public Samples allSamples() {
+ return allSamples;
+ }
+
+
+ @Override
+ public GL targetGL() {
+ return targetEmissions;
+ }
+
+ @Override
+ public List<HapPair> restrictedRefHapPairs() {
+ return new ArrayList<>(targetRefHapPairs);
+ }
+
+ @Override
+ public List<HapPair> refHapPairs() {
+ return new ArrayList<>(refHapPairs);
+ }
+
+ @Override
+ public SampleHapPairs refSampleHapPairs() {
+ return refSampleHapPairs;
+ }
+
+ @Override
+ public void close() {
+ refWindow.close();
+ targetWindow.close();
+ }
+
+ /**
+ * Returns a string representation of {@code this}. The exact
+ * details of the representation are unspecified and subject to change.
+ * @return a string representation of {@code this}
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(20);
+ sb.append(this.getClass().toString());
+ return sb.toString();
+ }
+}
diff --git a/vcf/GprobsStatistics.java b/vcf/GprobsStatistics.java
index d03a057..f45c903 100644
--- a/vcf/GprobsStatistics.java
+++ b/vcf/GprobsStatistics.java
@@ -40,16 +40,18 @@ import main.GenotypeValues;
*/
public class GprobsStatistics {
+ private static final double MIN_R2_DEN = 1e-8;
+
private final Marker marker;
private final int nSamples;
- private final float[] alleleFreq;
+ private final double[] alleleFreq;
- private float sumCall = 0;
- private float sumSquareCall = 0;
- private float sumExpected = 0;
- private float sumExpectedSquare = 0;
- private float sumSquareExpected= 0;
- private float sumCallExpected = 0;
+ private double sumCall = 0;
+ private double sumSquareCall = 0;
+ private double sumExpected = 0;
+ private double sumExpectedSquare = 0;
+ private double sumSquareExpected= 0;
+ private double sumCallExpected = 0;
/**
* Constructs a new {@code GprobsStatistics} instance from the
@@ -64,17 +66,17 @@ public class GprobsStatistics {
int nAlleles = gv.marker(marker).nAlleles();
this.marker = gv.marker(marker);
this.nSamples = gv.nSamples();
- this.alleleFreq = new float[nAlleles];
- float[] alProbs = new float[nAlleles];
- float[] gtProbs = new float[3];
+ this.alleleFreq = new double[nAlleles];
+ double[] alProbs = new double[nAlleles];
+ double[] gtProbs = new double[3];
for (int j=0; j<this.nSamples; ++j) {
setProbs(gv, marker, j, gtProbs, alProbs);
for (int a=0; a<nAlleles; ++a) {
alleleFreq[a] += alProbs[a];
}
int call = maxIndex(gtProbs);
- float exp = (gtProbs[1] + 2*gtProbs[2]);
- float expSquare = (gtProbs[1] + 4*gtProbs[2]);
+ double exp = (gtProbs[1] + 2*gtProbs[2]);
+ double expSquare = (gtProbs[1] + 4*gtProbs[2]);
sumCall += call;
sumSquareCall += call*call;
sumExpected += exp;
@@ -82,12 +84,12 @@ public class GprobsStatistics {
sumSquareExpected += (exp*exp);
sumCallExpected += (call*exp);
}
- float sum = sum(alleleFreq);
+ double sum = sum(alleleFreq);
divideBy(alleleFreq, sum);
}
private static void setProbs(GenotypeValues gv, int marker, int sample,
- float[] gtProbs, float[] alProbs) {
+ double[] gtProbs, double[] alProbs) {
Arrays.fill(gtProbs, 0.0f);
Arrays.fill(alProbs, 0.0f);
int gt = 0;
@@ -107,7 +109,7 @@ public class GprobsStatistics {
}
}
}
- float sum = sum(gtProbs);
+ double sum = sum(gtProbs);
divideBy(gtProbs, sum);
divideBy(alProbs, 2*sum);
}
@@ -125,17 +127,17 @@ public class GprobsStatistics {
int nAlleles = alleleProbs.marker(marker).nAlleles();
this.marker = alleleProbs.marker(marker);
this.nSamples = alleleProbs.nSamples();
- this.alleleFreq = new float[nAlleles];
- float[] alProbs = new float[nAlleles];
- float[] gtProbs = new float[3];
+ this.alleleFreq = new double[nAlleles];
+ double[] alProbs = new double[nAlleles];
+ double[] gtProbs = new double[3];
for (int j=0; j<this.nSamples; ++j) {
setProbs(alleleProbs, marker, j, gtProbs, alProbs);
for (int a=0; a<nAlleles; ++a) {
alleleFreq[a] += alProbs[a];
}
int call = maxIndex(gtProbs);
- float exp = (gtProbs[1] + 2*gtProbs[2]);
- float expSquare = (gtProbs[1] + 4*gtProbs[2]);
+ double exp = (gtProbs[1] + 2*gtProbs[2]);
+ double expSquare = (gtProbs[1] + 4*gtProbs[2]);
sumCall += call;
sumSquareCall += call*call;
sumExpected += exp;
@@ -143,17 +145,17 @@ public class GprobsStatistics {
sumSquareExpected += (exp*exp);
sumCallExpected += (call*exp);
}
- float sum = sum(alleleFreq);
+ double sum = sum(alleleFreq);
divideBy(alleleFreq, sum);
}
private static void setProbs(AlleleProbs ap, int marker, int sample,
- float[] gtProbs, float[] alProbs) {
+ double[] gtProbs, double[] alProbs) {
Arrays.fill(gtProbs, 0.0f);
Arrays.fill(alProbs, 0.0f);
for (int a2=0; a2<alProbs.length; ++a2) {
for (int a1=0; a1<=a2; ++a1) {
- float gprob = ap.gtProb(marker, sample, a1, a2);
+ double gprob = ap.gtProb(marker, sample, a1, a2);
if (a1 != a2) {
gprob += ap.gtProb(marker, sample, a2, a1);
}
@@ -170,12 +172,12 @@ public class GprobsStatistics {
}
}
}
- float sum = sum(gtProbs);
+ double sum = sum(gtProbs);
divideBy(gtProbs, sum);
divideBy(alProbs, 2*sum);
}
- private static int maxIndex(float[] fa) {
+ private static int maxIndex(double[] fa) {
int maxIndex = 0;
for (int j=1; j<fa.length; ++j) {
if (fa[j]>fa[maxIndex]) {
@@ -185,15 +187,15 @@ public class GprobsStatistics {
return maxIndex;
}
- private static float sum(float[] fa) {
- float sum = 0.0f;
- for (float f : fa) {
+ private static double sum(double[] fa) {
+ double sum = 0.0f;
+ for (double f : fa) {
sum += f;
}
return sum;
}
- private static void divideBy(float[] fa, float divisor) {
+ private static void divideBy(double[] fa, double divisor) {
for (int j=0; j<fa.length; ++j) {
fa[j] /= divisor;
}
@@ -215,7 +217,7 @@ public class GprobsStatistics {
* {@code j}-th element is the estimated sample frequency of allele
* {@code j}
*/
- public float[] alleleFreq() {
+ public double[] alleleFreq() {
return alleleFreq.clone();
}
@@ -228,13 +230,13 @@ public class GprobsStatistics {
* @return the estimated squared correlation between the most likely
* allele dose and the true allele dose
*/
- public float allelicR2() {
- float f = 1.0f / nSamples;
- float cov = sumCallExpected - (sumCall * sumExpected * f);
- float varBest = sumSquareCall - (sumCall * sumCall * f);
- float varExp = sumExpectedSquare - (sumExpected * sumExpected * f);
- float den = varBest * varExp;
- return (den==0.0f) ? 0.0f : Math.abs( (cov*cov) / den );
+ public double allelicR2() {
+ double f = 1.0f / nSamples;
+ double cov = sumCallExpected - (sumCall * sumExpected * f);
+ double varBest = sumSquareCall - (sumCall * sumCall * f);
+ double varExp = sumExpectedSquare - (sumExpected * sumExpected * f);
+ double den = varBest*varExp;
+ return (den < MIN_R2_DEN) ? 0.0f : (cov*cov/den);
}
/**
@@ -245,11 +247,14 @@ public class GprobsStatistics {
* @return the estimated squared correlation between the estimated
* ALT allele dose and the true ALT allele dose
*/
- public float doseR2() {
- float f = 1.0f / (float) nSamples;
- float num = sumSquareExpected - (sumExpected * sumExpected * f);
- float den = sumExpectedSquare - (sumExpected * sumExpected * f);
- return (den==0.0f) ? 0.0f : Math.abs(num / den);
+ public double doseR2() {
+ double f = 1.0f / (double) nSamples;
+ double num = sumSquareExpected - (sumExpected * sumExpected * f);
+ double den = sumExpectedSquare - (sumExpected * sumExpected * f);
+ if (num < 0.0) {
+ num = 0.0;
+ }
+ return (den < MIN_R2_DEN) ? 0.0f : (num / den);
}
/**
@@ -261,12 +266,15 @@ public class GprobsStatistics {
* @return the estimated squared correlation between the estimated
* ALT allele dose and the true ALT allele dose
*/
- public float hweDoseR2() {
- float f = 1.0f / nSamples;
- float num = (sumSquareExpected - (sumExpected*sumExpected*f))/nSamples;
- float altFreq = sumExpected / (2.0f * nSamples);
- float den = 2.0f * altFreq * (1.0f - altFreq);
- return (den==0.0f) ? 0.0f : Math.abs(num / den);
+ public double hweDoseR2() {
+ double f = 1.0f / nSamples;
+ double altFreq = sumExpected / (2.0f * nSamples);
+ double num = (sumSquareExpected - (sumExpected*sumExpected*f))/nSamples;
+ double den = 2.0f * altFreq * (1.0f - altFreq);
+ if (num < 0.0) {
+ num = 0.0;
+ }
+ return (den < MIN_R2_DEN) ? 0.0f : (num/den);
}
/**
@@ -296,7 +304,7 @@ public class GprobsStatistics {
return sb.toString();
}
- private static String format(DecimalFormat df, float d) {
+ private static String format(DecimalFormat df, double d) {
if (Double.isNaN(d)) {
return "NaN";
}
diff --git a/vcf/Markers.java b/vcf/Markers.java
index 13b6645..0117396 100644
--- a/vcf/Markers.java
+++ b/vcf/Markers.java
@@ -1,389 +1,389 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package vcf;
-
-import beagleutil.ChromIds;
-import blbutil.Const;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * <p>Class {@code Markers} represent a list of markers in chromosome order.
- * </p>
- * <p>Instances of class {@code Markers} are immutable.
- * </p>
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public final class Markers {
-
- private final Set<Marker> markerSet;
-
- private final Marker[] fwdMarkerArray;
- private final int[] fwdSumAlleles;
- private final int[] fwdSumGenotypes;
- private final int[] fwdSumHaplotypeBits;
- private final int fwdHashCode;
-
- private final Marker[] bwdMarkerArray;
- private final int[] bwdSumAlleles;
- private final int[] bwdSumGenotypes;
- private final int[] bwdSumHaplotypeBits;
- private final int bwdHashCode;
- private final Markers bwdMarkers;
-
- /**
- * Construct and return a new {@code Markers} instance that represents the
- * specified list of markers.
- * @param markers a list of markers in chromosome order
- * @return a new {@code Markers} instance that represents the
- * specified list of markers
- *
- * @throws IllegalArgumentException if markers on a chromosome are not
- * in chromosome order
- * @throws IllegalArgumentException if there are duplicate markers
- * @throws IllegalArgumentException if the markers on a chromosome
- * do not form a contiguous set of entries within the array
- *
- * @throws NullPointerException if
- * {@code markers == null} or if {@code markers[j] == null}
- * for any {@code j} satisfying {@code (0 <= j && j < markers.length)}
- */
- public static Markers create(Marker[] markers) {
- Markers fwd = new Markers(markers);
- return new Markers(fwd.reverse());
- }
-
- /**
- * Construct a new {@code Markers} instance that represents the
- * specified list of markers.
- * @param markers a list of markers in chromosome order
- *
- * @throws IllegalArgumentException if markers on a chromosome are not
- * in chromosome order
- * @throws IllegalArgumentException if there are duplicate markers
- * @throws IllegalArgumentException if the markers on a chromosome
- * do not form a contiguous set of entries within the array
- *
- * @throws NullPointerException if
- * {@code markers == null} or if {@code markers[j] == null}
- * for any {@code j} satisfying {@code (0 <= j && j < markers.length)}
- */
- private Markers(Marker[] markers) {
- checkMarkerPosOrder(markers);
- this.fwdMarkerArray = markers.clone();
- this.bwdMarkerArray = reverse(this.fwdMarkerArray);
- this.markerSet = markerSet(fwdMarkerArray);
-
- this.fwdSumAlleles = cumSumAlleles(fwdMarkerArray);
- this.fwdSumGenotypes = cumSumGenotypes(fwdMarkerArray);
- this.fwdSumHaplotypeBits = cumSumHaplotypeBits(fwdMarkerArray);
- this.fwdHashCode = Arrays.deepHashCode(fwdMarkerArray);
-
- this.bwdSumAlleles = cumSumAlleles(bwdMarkerArray);
- this.bwdSumGenotypes = cumSumGenotypes(bwdMarkerArray);
- this.bwdSumHaplotypeBits = cumSumHaplotypeBits(bwdMarkerArray);
- this.bwdHashCode = Arrays.deepHashCode(bwdMarkerArray);
- this.bwdMarkers = null;
- }
-
- /**
- * Constructs a new {@code Markers} instance whose {@code reverse()}
- * method returns the specified {@code Markers}
- * @param bwdMarkers a list of markers
- */
- private Markers(Markers bwdMarkers) {
- this.markerSet = bwdMarkers.markerSet;
- this.fwdMarkerArray = bwdMarkers.bwdMarkerArray;
- this.bwdMarkerArray = bwdMarkers.fwdMarkerArray;
-
- this.fwdSumAlleles = bwdMarkers.bwdSumAlleles;
- this.fwdSumGenotypes = bwdMarkers.bwdSumGenotypes;
- this.fwdSumHaplotypeBits = bwdMarkers.bwdSumHaplotypeBits;
- this.fwdHashCode = bwdMarkers.bwdHashCode;
-
- this.bwdSumAlleles = bwdMarkers.fwdSumAlleles;
- this.bwdSumGenotypes = bwdMarkers.fwdSumGenotypes;
- this.bwdSumHaplotypeBits = bwdMarkers.fwdSumHaplotypeBits;
- this.bwdHashCode = bwdMarkers.fwdHashCode;
- this.bwdMarkers = bwdMarkers;
- }
-
- private static void checkMarkerPosOrder(Marker[] markers) {
- if (markers.length < 2) {
- return;
- }
- Set<Integer> chromIndices = new HashSet<>();
- chromIndices.add(markers[0].chromIndex());
- chromIndices.add(markers[1].chromIndex());
- for (int j=2; j<markers.length; ++j) {
- int chr0 = markers[j-2].chromIndex();
- int chr1 = markers[j-1].chromIndex();
- int chr2 = markers[j].chromIndex();
- if (chr0 == chr1 && chr1==chr2) {
- int pos0 = markers[j-2].pos();
- int pos1 = markers[j-1].pos();
- int pos2 = markers[j].pos();
- if ( (pos1<pos0 && pos1<pos2) || (pos1>pos0 && pos1>pos2) ) {
- String s = "markers not in chromosomal order: "
- + Const.nl + markers[j-2]
- + Const.nl + markers[j-1]
- + Const.nl + markers[j];
- throw new IllegalArgumentException(s);
- }
- }
- else if (chr1!=chr2) {
- if (chromIndices.contains(chr2)) {
- String s = "markers on chromosome are not contiguous: "
- + ChromIds.instance().id(chr2);
- throw new IllegalArgumentException(s);
- }
- chromIndices.add(chr2);
- }
- }
- }
-
- private static Marker[] reverse(Marker[] markers) {
- int lastIndex = markers.length - 1;
- Marker[] rev = new Marker[markers.length];
- for (int j=0; j<markers.length; ++j) {
- rev[j] = markers[lastIndex - j];
- }
- return rev;
- }
-
- private static Set<Marker> markerSet(Marker[] markers) {
- Set<Marker> markerSet = new HashSet<>(markers.length);
- for (Marker m : markers) {
- if (markerSet.add(m)==false) {
- throw new IllegalArgumentException("Duplicate marker: " + m);
- }
- }
- return markerSet;
- }
-
- private static int[] cumSumAlleles(Marker[] markers) {
- int[] ia = new int[markers.length + 1];
- for (int j=1; j<ia.length; ++j) {
- ia[j] = ia[j-1] + markers[j-1].nAlleles();
- }
- return ia;
- }
-
- private static int[] cumSumGenotypes(Marker[] markers) {
- int[] ia = new int[markers.length + 1];
- for (int j=1; j<ia.length; ++j) {
- ia[j] = ia[j-1] + markers[j-1].nGenotypes();
- }
- return ia;
- }
-
- private static int[] cumSumHaplotypeBits(Marker[] markers) {
- int[] ia = new int[markers.length + 1];
- for (int j=1; j<ia.length; ++j) {
- int nAllelesM1 = markers[j-1].nAlleles() - 1;
- int nStorageBits = Integer.SIZE
- - Integer.numberOfLeadingZeros(nAllelesM1);
- ia[j] = ia[j-1] + nStorageBits;
- }
- return ia;
- }
-
- /**
- * Returns a hash code value for the object.
- * The returned hash code equals
- * {@code Arrays.deepHashCode(this.markers())}.
- * @return a hash code value for the object
- */
- @Override
- public int hashCode() {
- return fwdHashCode;
- }
-
- /**
- * Returns {@code true} if the specified object is a {@code Markers}
- * instance which represents the same list of markers as {@code this},
- * and returns {@code false} otherwise. Two lists of markers are
- * the same if the lists have the same size and if markers with the
- * same index in the two lists are equal.
- *
- * @param obj the object to be tested for equality with {@code this}
- *
- * @return {@code true} if the specified object is a {@code Markers}
- * instance which represents the same list of markers as {@code this}
- */
- @Override
- public boolean equals(Object obj) {
- if (this==obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final Markers other = (Markers) obj;
- return Arrays.deepEquals(this.fwdMarkerArray, other.fwdMarkerArray);
- }
-
- /**
- * Constructs and returns a new {@code Markers} instance that is
- * obtained by reversing the order of markers in {@code this}.
- * @return a new {@code Markers} instance that is obtained by
- * reversing the order of markers in {@code this}
- */
- public Markers reverse() {
- return bwdMarkers==null ? new Markers(this) : bwdMarkers;
- }
-
- /**
- * Returns the number of markers.
- * @return the number of markers
- */
- public int nMarkers() {
- return fwdMarkerArray.length;
- }
-
- /**
- * Returns the specified marker.
- * @param marker a marker index
- * @return the specified marker
- * @throws IndexOutOfBoundsException if
- * {@code marker < 0 || marker >= this.nMarkers()}
- */
- public Marker marker(int marker) {
- return fwdMarkerArray[marker];
- }
-
- /**
- * Returns the list of markers.
- * @return the list of markers
- */
- public Marker[] markers() {
- return fwdMarkerArray.clone();
- }
-
- /**
- * Returns {@code true} if the specified marker is not {@code null}
- * and is an element in the list of markers represented by {@code this},
- * and returns {@code false} otherwise.
- *
- * @param marker a marker
- *
- * @return {@code true} if the specified marker is not {@code null} and
- * is an element in the list of markers represented by {@code this}
- */
- public boolean contains(Marker marker) {
- return markerSet.contains(marker);
- }
-
- /**
- * Returns a {@code Markers} instance that represents
- * the specified range of marker indices.
- * @param start the starting marker index (inclusive)
- * @param end the ending marker index (exclusive)
- * @return a {@code Markers} instance that represents
- * the specified range of marker indices
- *
- * @throws IndexOutOfBoundsException if
- * {@code start < 0 || end > this.nMarkers()}
- * @throws IllegalArgumentException if {@code start >= end}.
- */
- public Markers restrict(int start, int end) {
- if (end > fwdMarkerArray.length) {
- throw new IndexOutOfBoundsException("end > this.nMarkers(): " + end);
- }
- return new Markers(Arrays.copyOfRange(fwdMarkerArray, start, end));
- }
-
- /**
- * Returns the sum of the number of alleles for
- * the markers with index less than the specified index.
- * @param marker a marker index
- * @return the sum of the number of alleles for
- * the markers with index less than the specified index
- * @throws IndexOutOfBoundsException if
- * {@code marker < 0 || marker > this.nMarkers()}
- */
- public int sumAlleles(int marker) {
- return fwdSumAlleles[marker];
- }
-
- /**
- * Returns {@code this.sumAlleles(this.nMarkers())}.
- * @return {@code this.sumAlleles(this.nMarkers())}
- */
- public int sumAlleles() {
- return fwdSumAlleles[fwdMarkerArray.length];
- }
-
- /**
- * Returns the sum of the number of possible genotypes for the markers
- * with index less than the specified index.
- * @param marker a marker index
- * @return the sum of the number of possible genotypes for the markers
- * with index less than the specified index
- * @throws IndexOutOfBoundsException if
- * {@code marker < 0 || marker > this.nMarkers()}
- */
- public int sumGenotypes(int marker) {
- return fwdSumGenotypes[marker];
- }
-
- /**
- * Returns {@code this.sumGenotypes(this.nMarkers())}.
- * @return {@code this.sumGenotypes(this.nMarkers())}
- */
- public int sumGenotypes() {
- return fwdSumGenotypes[fwdMarkerArray.length];
- }
-
- /**
- * Returns the number of bits requires to store a haplotype for the
- * markers with index less than the specified index.
- * @param marker a marker index
- * @return the number of bits requires to store a haplotype for the
- * markers with index less than the specified index
- * @throws IndexOutOfBoundsException if
- * {@code marker < 0 || marker > this.nMarkers()}
- */
- public int sumHaplotypeBits(int marker) {
- return fwdSumHaplotypeBits[marker];
- }
-
- /**
- * Returns {@code this.sumHaplotypeBits(this.nMarkers())}.
- * @return {@code this.sumHaplotypeBits(this.nMarkers())}
- */
- public int sumHaplotypeBits() {
- return fwdSumHaplotypeBits[fwdMarkerArray.length];
- }
-
- /**
- * Returns a string representation of {@code this}.
- * The exact details of the representation are unspecified and
- * subject to change.
- * @return a string representation of {@code this}
- */
- @Override
- public String toString() {
- return Arrays.toString(fwdMarkerArray);
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package vcf;
+
+import beagleutil.ChromIds;
+import blbutil.Const;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * <p>Class {@code Markers} represent a list of markers in chromosome order.
+ * </p>
+ * <p>Instances of class {@code Markers} are immutable.
+ * </p>
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public final class Markers {
+
+ private final Set<Marker> markerSet;
+
+ private final Marker[] fwdMarkerArray;
+ private final int[] fwdSumAlleles;
+ private final int[] fwdSumGenotypes;
+ private final int[] fwdSumHaplotypeBits;
+ private final int fwdHashCode;
+
+ private final Marker[] bwdMarkerArray;
+ private final int[] bwdSumAlleles;
+ private final int[] bwdSumGenotypes;
+ private final int[] bwdSumHaplotypeBits;
+ private final int bwdHashCode;
+ private final Markers bwdMarkers;
+
+ /**
+ * Construct and return a new {@code Markers} instance that represents the
+ * specified list of markers.
+ * @param markers a list of markers in chromosome order
+ * @return a new {@code Markers} instance that represents the
+ * specified list of markers
+ *
+ * @throws IllegalArgumentException if markers on a chromosome are not
+ * in chromosome order
+ * @throws IllegalArgumentException if there are duplicate markers
+ * @throws IllegalArgumentException if the markers on a chromosome
+ * do not form a contiguous set of entries within the array
+ *
+ * @throws NullPointerException if
+ * {@code markers == null} or if {@code markers[j] == null}
+ * for any {@code j} satisfying {@code (0 <= j && j < markers.length)}
+ */
+ public static Markers create(Marker[] markers) {
+ Markers fwd = new Markers(markers);
+ return new Markers(fwd.reverse());
+ }
+
+ /**
+ * Construct a new {@code Markers} instance that represents the
+ * specified list of markers.
+ * @param markers a list of markers in chromosome order
+ *
+ * @throws IllegalArgumentException if markers on a chromosome are not
+ * in chromosome order
+ * @throws IllegalArgumentException if there are duplicate markers
+ * @throws IllegalArgumentException if the markers on a chromosome
+ * do not form a contiguous set of entries within the array
+ *
+ * @throws NullPointerException if
+ * {@code markers == null} or if {@code markers[j] == null}
+ * for any {@code j} satisfying {@code (0 <= j && j < markers.length)}
+ */
+ private Markers(Marker[] markers) {
+ checkMarkerPosOrder(markers);
+ this.fwdMarkerArray = markers.clone();
+ this.bwdMarkerArray = reverse(this.fwdMarkerArray);
+ this.markerSet = markerSet(fwdMarkerArray);
+
+ this.fwdSumAlleles = cumSumAlleles(fwdMarkerArray);
+ this.fwdSumGenotypes = cumSumGenotypes(fwdMarkerArray);
+ this.fwdSumHaplotypeBits = cumSumHaplotypeBits(fwdMarkerArray);
+ this.fwdHashCode = Arrays.deepHashCode(fwdMarkerArray);
+
+ this.bwdSumAlleles = cumSumAlleles(bwdMarkerArray);
+ this.bwdSumGenotypes = cumSumGenotypes(bwdMarkerArray);
+ this.bwdSumHaplotypeBits = cumSumHaplotypeBits(bwdMarkerArray);
+ this.bwdHashCode = Arrays.deepHashCode(bwdMarkerArray);
+ this.bwdMarkers = null;
+ }
+
+ /**
+ * Constructs a new {@code Markers} instance whose {@code reverse()}
+ * method returns the specified {@code Markers}
+ * @param bwdMarkers a list of markers
+ */
+ private Markers(Markers bwdMarkers) {
+ this.markerSet = bwdMarkers.markerSet;
+ this.fwdMarkerArray = bwdMarkers.bwdMarkerArray;
+ this.bwdMarkerArray = bwdMarkers.fwdMarkerArray;
+
+ this.fwdSumAlleles = bwdMarkers.bwdSumAlleles;
+ this.fwdSumGenotypes = bwdMarkers.bwdSumGenotypes;
+ this.fwdSumHaplotypeBits = bwdMarkers.bwdSumHaplotypeBits;
+ this.fwdHashCode = bwdMarkers.bwdHashCode;
+
+ this.bwdSumAlleles = bwdMarkers.fwdSumAlleles;
+ this.bwdSumGenotypes = bwdMarkers.fwdSumGenotypes;
+ this.bwdSumHaplotypeBits = bwdMarkers.fwdSumHaplotypeBits;
+ this.bwdHashCode = bwdMarkers.fwdHashCode;
+ this.bwdMarkers = bwdMarkers;
+ }
+
+ private static void checkMarkerPosOrder(Marker[] markers) {
+ if (markers.length < 2) {
+ return;
+ }
+ Set<Integer> chromIndices = new HashSet<>();
+ chromIndices.add(markers[0].chromIndex());
+ chromIndices.add(markers[1].chromIndex());
+ for (int j=2; j<markers.length; ++j) {
+ int chr0 = markers[j-2].chromIndex();
+ int chr1 = markers[j-1].chromIndex();
+ int chr2 = markers[j].chromIndex();
+ if (chr0 == chr1 && chr1==chr2) {
+ int pos0 = markers[j-2].pos();
+ int pos1 = markers[j-1].pos();
+ int pos2 = markers[j].pos();
+ if ( (pos1<pos0 && pos1<pos2) || (pos1>pos0 && pos1>pos2) ) {
+ String s = "markers not in chromosomal order: "
+ + Const.nl + markers[j-2]
+ + Const.nl + markers[j-1]
+ + Const.nl + markers[j];
+ throw new IllegalArgumentException(s);
+ }
+ }
+ else if (chr1!=chr2) {
+ if (chromIndices.contains(chr2)) {
+ String s = "markers on chromosome are not contiguous: "
+ + ChromIds.instance().id(chr2);
+ throw new IllegalArgumentException(s);
+ }
+ chromIndices.add(chr2);
+ }
+ }
+ }
+
+ private static Marker[] reverse(Marker[] markers) {
+ int lastIndex = markers.length - 1;
+ Marker[] rev = new Marker[markers.length];
+ for (int j=0; j<markers.length; ++j) {
+ rev[j] = markers[lastIndex - j];
+ }
+ return rev;
+ }
+
+ private static Set<Marker> markerSet(Marker[] markers) {
+ Set<Marker> markerSet = new HashSet<>(markers.length);
+ for (Marker m : markers) {
+ if (markerSet.add(m)==false) {
+ throw new IllegalArgumentException("Duplicate marker: " + m);
+ }
+ }
+ return markerSet;
+ }
+
+ private static int[] cumSumAlleles(Marker[] markers) {
+ int[] ia = new int[markers.length + 1];
+ for (int j=1; j<ia.length; ++j) {
+ ia[j] = ia[j-1] + markers[j-1].nAlleles();
+ }
+ return ia;
+ }
+
+ private static int[] cumSumGenotypes(Marker[] markers) {
+ int[] ia = new int[markers.length + 1];
+ for (int j=1; j<ia.length; ++j) {
+ ia[j] = ia[j-1] + markers[j-1].nGenotypes();
+ }
+ return ia;
+ }
+
+ private static int[] cumSumHaplotypeBits(Marker[] markers) {
+ int[] ia = new int[markers.length + 1];
+ for (int j=1; j<ia.length; ++j) {
+ int nAllelesM1 = markers[j-1].nAlleles() - 1;
+ int nStorageBits = Integer.SIZE
+ - Integer.numberOfLeadingZeros(nAllelesM1);
+ ia[j] = ia[j-1] + nStorageBits;
+ }
+ return ia;
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ * The returned hash code equals
+ * {@code Arrays.deepHashCode(this.markers())}.
+ * @return a hash code value for the object
+ */
+ @Override
+ public int hashCode() {
+ return fwdHashCode;
+ }
+
+ /**
+ * Returns {@code true} if the specified object is a {@code Markers}
+ * instance which represents the same list of markers as {@code this},
+ * and returns {@code false} otherwise. Two lists of markers are
+ * the same if the lists have the same size and if markers with the
+ * same index in the two lists are equal.
+ *
+ * @param obj the object to be tested for equality with {@code this}
+ *
+ * @return {@code true} if the specified object is a {@code Markers}
+ * instance which represents the same list of markers as {@code this}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this==obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Markers other = (Markers) obj;
+ return Arrays.deepEquals(this.fwdMarkerArray, other.fwdMarkerArray);
+ }
+
+ /**
+ * Constructs and returns a new {@code Markers} instance that is
+ * obtained by reversing the order of markers in {@code this}.
+ * @return a new {@code Markers} instance that is obtained by
+ * reversing the order of markers in {@code this}
+ */
+ public Markers reverse() {
+ return bwdMarkers==null ? new Markers(this) : bwdMarkers;
+ }
+
+ /**
+ * Returns the number of markers.
+ * @return the number of markers
+ */
+ public int nMarkers() {
+ return fwdMarkerArray.length;
+ }
+
+ /**
+ * Returns the specified marker.
+ * @param marker a marker index
+ * @return the specified marker
+ * @throws IndexOutOfBoundsException if
+ * {@code marker < 0 || marker >= this.nMarkers()}
+ */
+ public Marker marker(int marker) {
+ return fwdMarkerArray[marker];
+ }
+
+ /**
+ * Returns the list of markers.
+ * @return the list of markers
+ */
+ public Marker[] markers() {
+ return fwdMarkerArray.clone();
+ }
+
+ /**
+ * Returns {@code true} if the specified marker is not {@code null}
+ * and is an element in the list of markers represented by {@code this},
+ * and returns {@code false} otherwise.
+ *
+ * @param marker a marker
+ *
+ * @return {@code true} if the specified marker is not {@code null} and
+ * is an element in the list of markers represented by {@code this}
+ */
+ public boolean contains(Marker marker) {
+ return markerSet.contains(marker);
+ }
+
+ /**
+ * Returns a {@code Markers} instance that represents
+ * the specified range of marker indices.
+ * @param start the starting marker index (inclusive)
+ * @param end the ending marker index (exclusive)
+ * @return a {@code Markers} instance that represents
+ * the specified range of marker indices
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code start < 0 || end > this.nMarkers()}
+ * @throws IllegalArgumentException if {@code start >= end}.
+ */
+ public Markers restrict(int start, int end) {
+ if (end > fwdMarkerArray.length) {
+ throw new IndexOutOfBoundsException("end > this.nMarkers(): " + end);
+ }
+ return new Markers(Arrays.copyOfRange(fwdMarkerArray, start, end));
+ }
+
+ /**
+ * Returns the sum of the number of alleles for
+ * the markers with index less than the specified index.
+ * @param marker a marker index
+ * @return the sum of the number of alleles for
+ * the markers with index less than the specified index
+ * @throws IndexOutOfBoundsException if
+ * {@code marker < 0 || marker > this.nMarkers()}
+ */
+ public int sumAlleles(int marker) {
+ return fwdSumAlleles[marker];
+ }
+
+ /**
+ * Returns {@code this.sumAlleles(this.nMarkers())}.
+ * @return {@code this.sumAlleles(this.nMarkers())}
+ */
+ public int sumAlleles() {
+ return fwdSumAlleles[fwdMarkerArray.length];
+ }
+
+ /**
+ * Returns the sum of the number of possible genotypes for the markers
+ * with index less than the specified index.
+ * @param marker a marker index
+ * @return the sum of the number of possible genotypes for the markers
+ * with index less than the specified index
+ * @throws IndexOutOfBoundsException if
+ * {@code marker < 0 || marker > this.nMarkers()}
+ */
+ public int sumGenotypes(int marker) {
+ return fwdSumGenotypes[marker];
+ }
+
+ /**
+ * Returns {@code this.sumGenotypes(this.nMarkers())}.
+ * @return {@code this.sumGenotypes(this.nMarkers())}
+ */
+ public int sumGenotypes() {
+ return fwdSumGenotypes[fwdMarkerArray.length];
+ }
+
+ /**
+ * Returns the number of bits requires to store a haplotype for the
+ * markers with index less than the specified index.
+ * @param marker a marker index
+ * @return the number of bits requires to store a haplotype for the
+ * markers with index less than the specified index
+ * @throws IndexOutOfBoundsException if
+ * {@code marker < 0 || marker > this.nMarkers()}
+ */
+ public int sumHaplotypeBits(int marker) {
+ return fwdSumHaplotypeBits[marker];
+ }
+
+ /**
+ * Returns {@code this.sumHaplotypeBits(this.nMarkers())}.
+ * @return {@code this.sumHaplotypeBits(this.nMarkers())}
+ */
+ public int sumHaplotypeBits() {
+ return fwdSumHaplotypeBits[fwdMarkerArray.length];
+ }
+
+ /**
+ * Returns a string representation of {@code this}.
+ * The exact details of the representation are unspecified and
+ * subject to change.
+ * @return a string representation of {@code this}
+ */
+ @Override
+ public String toString() {
+ return Arrays.toString(fwdMarkerArray);
+ }
+}
diff --git a/vcf/VcfWindow.java b/vcf/VcfWindow.java
index 4750286..8fc44f5 100644
--- a/vcf/VcfWindow.java
+++ b/vcf/VcfWindow.java
@@ -1,321 +1,321 @@
-/*
- * Copyright (C) 2014 Brian L. Browning
- *
- * This file is part of Beagle
- *
- * Beagle 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.
- *
- * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package vcf;
-
-import beagleutil.Samples;
-import blbutil.SampleFileIt;
-import java.io.Closeable;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import main.GeneticMap;
-
-/**
- * <p>Class {@code VcfWindow} represents a sliding window of VCF records.
- * </p>
- * Instances of class {@code VcfWindow} are not thread-safe.
- *
- * @author Brian L. Browning {@code <browning at uw.edu>}
- */
-public class VcfWindow implements Closeable {
-
- private final SampleFileIt<? extends VcfEmission> it;
- private final List<VcfEmission> window;
- private int overlap;
- private int cumMarkerCnt;
- private VcfEmission next;
-
- /**
- * Constructs a new {@code VcfWindow} instance.
- * @param it an iterator that returns VCF records
- * @throws IllegalArgumentException if {@code it.hasNext() == false}
- * @throws IllegalArgumentException if a format error is detected in
- * a VCF record
- * @throws NullPointerException if {@code it == null}
- */
- public VcfWindow(SampleFileIt<? extends VcfEmission> it) {
- if (it.hasNext()==false) {
- throw new IllegalArgumentException("it.hasNext()==false");
- }
- this.it = it;
- this.overlap = 0;
- this.cumMarkerCnt = 0;
- this.window = new ArrayList<>(20000);
- this.next = it.next();
- }
-
- /**
- * Returns {@code true} if the sliding window of VCF Records is the last
- * window for the chromosome and returns {@code false} otherwise.
- * @return {@code true} if the sliding window of VCF Records is the last
- * window for the chromosome
- */
- public boolean lastWindowOnChrom() {
- return next==null || (sameChrom(next, window.get(0))==false);
- }
-
- private boolean sameChrom(VcfEmission a, VcfEmission b) {
- return a.marker().chromIndex()==b.marker().chromIndex();
- }
-
- /**
- * Returns {@code true} if the sliding window of VCF records can advance
- * and returns {@code false} otherwise.
- * @return {@code true} if the sliding window of VCF records can advance
- */
- public boolean canAdvanceWindow() {
- return next!=null;
- }
-
- /**
- * Advances the sliding window of VCF records, and returns the advanced
- * window as a {@code VcfEmission[]} object. The size of the advanced
- * window and the number of markers of overlap between the marker window
- * immediately before method invocation and the marker window immediately
- * after method invocation may differ from the requested values. If the
- * advanced window size or overlap is less than the requested value, the
- * actual value will be as large as possible. If
- * {@code this.lastWindowOnChrom() == true} before method invocation, then
- * there will be no overlap between the advanced window and the previous
- * window.
- *
- * @param overlap the requested number of markers of overlap
- * @param windowSize the requested number of the markers in the window
- * immediately after the method returns
- * @return the advanced window of VCF records
- *
- * @throws IllegalArgumentException if a format error is detected in
- * a VCF record
- * @throws IllegalArgumentException if
- * {@code overlap < 0 || overlap >= windowSize}
- * @throws IllegalStateException if
- * {@code this.canAdvanceWindow() == false}
- */
- public VcfEmission[] advanceWindow(int overlap, int windowSize) {
- if (canAdvanceWindow()==false) {
- throw new IllegalStateException("canAdvanceWindow()==false");
- }
- checkParameters(overlap, windowSize);
- overlap = getActualOverlap(overlap);
- List<VcfEmission> newWindow = new ArrayList<>(windowSize);
-
- newWindow.addAll(window.subList(window.size() - overlap, window.size()));
- int currentChromIndex = currentChromIndex(newWindow);
- while (newWindow.size() < windowSize
- && next != null
- && next.marker().chromIndex()==currentChromIndex) {
- newWindow.add(next);
- next = it.hasNext() ? it.next() : null;
- }
- // add all markers at the same marker position
- VcfEmission last = newWindow.get(newWindow.size()-1);
- while (next!=null && samePosition(last, next)) {
- newWindow.add(next);
- next = it.hasNext() ? it.next() : null;
- }
- this.overlap = overlap;
- this.window.clear();
- this.window.addAll(newWindow);
- this.cumMarkerCnt += (window.size() - overlap);
- return window.toArray(new VcfEmission[0]);
- }
-
- /**
- * Advances the sliding window of VCF records, and returns the advanced
- * window as a {@code VcfEmission[]} object. The size of the advanced
- * window and the number of markers of overlap between the marker window
- * immediately before method invocation and the marker window immediately
- * after method invocation may differ from the requested values. If the
- * distance the window is advanced or the overlap is less than the requested
- * value, the actual distance or overlap will be as large as possible. If
- * {@code this.lastWindowOnChrom() == true}
- * before method invocation, then there will be no overlap between the
- * advanced window and the previous window
- *
- * @param overlap the requested number of markers of overlap
- * @param cM the requested distance in cM to advance the window
- * @param map the genetic map
- * @return the advanced window of VCF records
- *
- * @throws IllegalArgumentException if a format error is detected in
- * a VCF record
- * @throws IllegalArgumentException if {@code overlap < 0 || cM <= 0}
- * @throws IllegalStateException if
- * {@code this.canAdvanceWindow() == false}
- */
- public VcfEmission[] advanceWindow(int overlap, double cM, GeneticMap map) {
- if (canAdvanceWindow()==false) {
- throw new IllegalStateException("canAdvanceWindow()==false");
- }
- if (overlap < 0) {
- throw new IllegalArgumentException(String.valueOf(overlap));
- }
- if (cM < 0) {
- throw new IllegalArgumentException(String.valueOf(cM));
- }
-
- overlap = getActualOverlap(overlap);
- List<VcfEmission> newWindow = new ArrayList<>(overlap + 1000);
-
- newWindow.addAll(window.subList(window.size() - overlap, window.size()));
- int currentChromIndex = currentChromIndex(newWindow);
- double endMapPos = startMapPos(newWindow, map) + cM;
- while (next != null
- && next.marker().chromIndex()==currentChromIndex
- && map.genPos(next.marker()) < endMapPos) {
- newWindow.add(next);
- next = it.hasNext() ? it.next() : null;
- }
- // add all markers at the same marker position
- VcfEmission last = newWindow.get(newWindow.size()-1);
- while (next!=null && samePosition(last, next)) {
- newWindow.add(next);
- next = it.hasNext() ? it.next() : null;
- }
- this.overlap = overlap;
- this.window.clear();
- this.window.addAll(newWindow);
- this.cumMarkerCnt += (window.size() - overlap);
- return window.toArray(new VcfEmission[0]);
- }
-
- private void checkParameters(int overlap, int windowSize) {
- if (overlap < 0 || overlap >= windowSize) {
- String s = "overlap=" + overlap + "windowSize=" + windowSize;
- throw new IllegalArgumentException(s);
- }
- }
-
- private int getActualOverlap(int overlap) {
- if (window.isEmpty() || lastWindowOnChrom()) {
- return 0;
- }
- int n = window.size();
- if (overlap > n) {
- overlap = n;
- }
- while (overlap > 0 && overlap < n
- && window.get(n - overlap).marker().pos()
- == window.get(n - overlap - 1).marker().pos()) {
- ++overlap;
- }
- return overlap;
- }
-
- private int currentChromIndex(List<VcfEmission> currentWindow) {
- if (currentWindow.isEmpty()==false) {
- return currentWindow.get(0).marker().chromIndex();
- }
- else if (next!=null) {
- return next.marker().chromIndex();
- }
- else {
- return -1;
- }
- }
-
- private double startMapPos(List<VcfEmission> currentWindow, GeneticMap map) {
- if (currentWindow.isEmpty()==false) {
- Marker m = currentWindow.get(currentWindow.size() - 1).marker();
- return map.genPos(m);
- }
- else if (next!=null) {
- return map.genPos(next.marker());
- }
- else {
- return 0;
- }
- }
-
- private boolean samePosition(VcfEmission a, VcfEmission b) {
- return a.marker().chromIndex()==b.marker().chromIndex()
- && a.marker().pos()==b.marker().pos();
- }
-
- /**
- * Returns the file from which VCF records are read, or returns
- * {@code null} if the source is standard input.
- * @return the file from which VCF records are read, or
- * {@code null} if the source is standard input
- */
- public File file() {
- return it.file();
- }
-
- /**
- * Returns the list of samples.
- * @return the list of samples
- */
- public Samples samples() {
- return it.samples();
- }
-
- /**
- * Returns the number of samples.
- * @return the number of samples
- */
- public int nSamples() {
- return it.samples().nSamples();
- }
-
- /**
- * Returns the number of VCF records in the overlap between the current
- * window and the previous window. Returns 0 if the current window
- * is the first window.
- *
- * @return the number of VCF records in the overlap between the current
- * window and the previous window
- */
- public int overlap() {
- return overlap;
- }
-
- /**
- * Returns the number of distinct VCF records in the union of the current
- * window and all previous windows.
- *
- * @return the number of distinct VCF records in the union of the current
- * window and all previous windows
- */
- public int cumMarkerCnt() {
- return cumMarkerCnt;
- }
-
- /**
- * Releases any I/O resources controlled by this object.
- */
- @Override
- public void close() {
- it.close();
- }
-
- /**
- * Returns a string representation of {@code this}. The exact
- * details of the representation are unspecified and subject to change.
- * @return a string representation of {@code this}
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(1100);
- sb.append(this.getClass().toString());
- sb.append("; next: ");
- sb.append(next);
- return sb.toString();
- }
-}
+/*
+ * Copyright (C) 2014 Brian L. Browning
+ *
+ * This file is part of Beagle
+ *
+ * Beagle 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.
+ *
+ * Beagle 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package vcf;
+
+import beagleutil.Samples;
+import blbutil.SampleFileIt;
+import java.io.Closeable;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import main.GeneticMap;
+
+/**
+ * <p>Class {@code VcfWindow} represents a sliding window of VCF records.
+ * </p>
+ * Instances of class {@code VcfWindow} are not thread-safe.
+ *
+ * @author Brian L. Browning {@code <browning at uw.edu>}
+ */
+public class VcfWindow implements Closeable {
+
+ private final SampleFileIt<? extends VcfEmission> it;
+ private final List<VcfEmission> window;
+ private int overlap;
+ private int cumMarkerCnt;
+ private VcfEmission next;
+
+ /**
+ * Constructs a new {@code VcfWindow} instance.
+ * @param it an iterator that returns VCF records
+ * @throws IllegalArgumentException if {@code it.hasNext() == false}
+ * @throws IllegalArgumentException if a format error is detected in
+ * a VCF record
+ * @throws NullPointerException if {@code it == null}
+ */
+ public VcfWindow(SampleFileIt<? extends VcfEmission> it) {
+ if (it.hasNext()==false) {
+ throw new IllegalArgumentException("it.hasNext()==false");
+ }
+ this.it = it;
+ this.overlap = 0;
+ this.cumMarkerCnt = 0;
+ this.window = new ArrayList<>(20000);
+ this.next = it.next();
+ }
+
+ /**
+ * Returns {@code true} if the sliding window of VCF Records is the last
+ * window for the chromosome and returns {@code false} otherwise.
+ * @return {@code true} if the sliding window of VCF Records is the last
+ * window for the chromosome
+ */
+ public boolean lastWindowOnChrom() {
+ return next==null || (sameChrom(next, window.get(0))==false);
+ }
+
+ private boolean sameChrom(VcfEmission a, VcfEmission b) {
+ return a.marker().chromIndex()==b.marker().chromIndex();
+ }
+
+ /**
+ * Returns {@code true} if the sliding window of VCF records can advance
+ * and returns {@code false} otherwise.
+ * @return {@code true} if the sliding window of VCF records can advance
+ */
+ public boolean canAdvanceWindow() {
+ return next!=null;
+ }
+
+ /**
+ * Advances the sliding window of VCF records, and returns the advanced
+ * window as a {@code VcfEmission[]} object. The size of the advanced
+ * window and the number of markers of overlap between the marker window
+ * immediately before method invocation and the marker window immediately
+ * after method invocation may differ from the requested values. If the
+ * advanced window size or overlap is less than the requested value, the
+ * actual value will be as large as possible. If
+ * {@code this.lastWindowOnChrom() == true} before method invocation, then
+ * there will be no overlap between the advanced window and the previous
+ * window.
+ *
+ * @param overlap the requested number of markers of overlap
+ * @param windowSize the requested number of the markers in the window
+ * immediately after the method returns
+ * @return the advanced window of VCF records
+ *
+ * @throws IllegalArgumentException if a format error is detected in
+ * a VCF record
+ * @throws IllegalArgumentException if
+ * {@code overlap < 0 || overlap >= windowSize}
+ * @throws IllegalStateException if
+ * {@code this.canAdvanceWindow() == false}
+ */
+ public VcfEmission[] advanceWindow(int overlap, int windowSize) {
+ if (canAdvanceWindow()==false) {
+ throw new IllegalStateException("canAdvanceWindow()==false");
+ }
+ checkParameters(overlap, windowSize);
+ overlap = getActualOverlap(overlap);
+ List<VcfEmission> newWindow = new ArrayList<>(windowSize);
+
+ newWindow.addAll(window.subList(window.size() - overlap, window.size()));
+ int currentChromIndex = currentChromIndex(newWindow);
+ while (newWindow.size() < windowSize
+ && next != null
+ && next.marker().chromIndex()==currentChromIndex) {
+ newWindow.add(next);
+ next = it.hasNext() ? it.next() : null;
+ }
+ // add all markers at the same marker position
+ VcfEmission last = newWindow.get(newWindow.size()-1);
+ while (next!=null && samePosition(last, next)) {
+ newWindow.add(next);
+ next = it.hasNext() ? it.next() : null;
+ }
+ this.overlap = overlap;
+ this.window.clear();
+ this.window.addAll(newWindow);
+ this.cumMarkerCnt += (window.size() - overlap);
+ return window.toArray(new VcfEmission[0]);
+ }
+
+ /**
+ * Advances the sliding window of VCF records, and returns the advanced
+ * window as a {@code VcfEmission[]} object. The size of the advanced
+ * window and the number of markers of overlap between the marker window
+ * immediately before method invocation and the marker window immediately
+ * after method invocation may differ from the requested values. If the
+ * distance the window is advanced or the overlap is less than the requested
+ * value, the actual distance or overlap will be as large as possible. If
+ * {@code this.lastWindowOnChrom() == true}
+ * before method invocation, then there will be no overlap between the
+ * advanced window and the previous window
+ *
+ * @param overlap the requested number of markers of overlap
+ * @param cM the requested distance in cM to advance the window
+ * @param map the genetic map
+ * @return the advanced window of VCF records
+ *
+ * @throws IllegalArgumentException if a format error is detected in
+ * a VCF record
+ * @throws IllegalArgumentException if {@code overlap < 0 || cM <= 0}
+ * @throws IllegalStateException if
+ * {@code this.canAdvanceWindow() == false}
+ */
+ public VcfEmission[] advanceWindow(int overlap, double cM, GeneticMap map) {
+ if (canAdvanceWindow()==false) {
+ throw new IllegalStateException("canAdvanceWindow()==false");
+ }
+ if (overlap < 0) {
+ throw new IllegalArgumentException(String.valueOf(overlap));
+ }
+ if (cM < 0) {
+ throw new IllegalArgumentException(String.valueOf(cM));
+ }
+
+ overlap = getActualOverlap(overlap);
+ List<VcfEmission> newWindow = new ArrayList<>(overlap + 1000);
+
+ newWindow.addAll(window.subList(window.size() - overlap, window.size()));
+ int currentChromIndex = currentChromIndex(newWindow);
+ double endMapPos = startMapPos(newWindow, map) + cM;
+ while (next != null
+ && next.marker().chromIndex()==currentChromIndex
+ && map.genPos(next.marker()) < endMapPos) {
+ newWindow.add(next);
+ next = it.hasNext() ? it.next() : null;
+ }
+ // add all markers at the same marker position
+ VcfEmission last = newWindow.get(newWindow.size()-1);
+ while (next!=null && samePosition(last, next)) {
+ newWindow.add(next);
+ next = it.hasNext() ? it.next() : null;
+ }
+ this.overlap = overlap;
+ this.window.clear();
+ this.window.addAll(newWindow);
+ this.cumMarkerCnt += (window.size() - overlap);
+ return window.toArray(new VcfEmission[0]);
+ }
+
+ private void checkParameters(int overlap, int windowSize) {
+ if (overlap < 0 || overlap >= windowSize) {
+ String s = "overlap=" + overlap + "windowSize=" + windowSize;
+ throw new IllegalArgumentException(s);
+ }
+ }
+
+ private int getActualOverlap(int overlap) {
+ if (window.isEmpty() || lastWindowOnChrom()) {
+ return 0;
+ }
+ int n = window.size();
+ if (overlap > n) {
+ overlap = n;
+ }
+ while (overlap > 0 && overlap < n
+ && window.get(n - overlap).marker().pos()
+ == window.get(n - overlap - 1).marker().pos()) {
+ ++overlap;
+ }
+ return overlap;
+ }
+
+ private int currentChromIndex(List<VcfEmission> currentWindow) {
+ if (currentWindow.isEmpty()==false) {
+ return currentWindow.get(0).marker().chromIndex();
+ }
+ else if (next!=null) {
+ return next.marker().chromIndex();
+ }
+ else {
+ return -1;
+ }
+ }
+
+ private double startMapPos(List<VcfEmission> currentWindow, GeneticMap map) {
+ if (currentWindow.isEmpty()==false) {
+ Marker m = currentWindow.get(currentWindow.size() - 1).marker();
+ return map.genPos(m);
+ }
+ else if (next!=null) {
+ return map.genPos(next.marker());
+ }
+ else {
+ return 0;
+ }
+ }
+
+ private boolean samePosition(VcfEmission a, VcfEmission b) {
+ return a.marker().chromIndex()==b.marker().chromIndex()
+ && a.marker().pos()==b.marker().pos();
+ }
+
+ /**
+ * Returns the file from which VCF records are read, or returns
+ * {@code null} if the source is standard input.
+ * @return the file from which VCF records are read, or
+ * {@code null} if the source is standard input
+ */
+ public File file() {
+ return it.file();
+ }
+
+ /**
+ * Returns the list of samples.
+ * @return the list of samples
+ */
+ public Samples samples() {
+ return it.samples();
+ }
+
+ /**
+ * Returns the number of samples.
+ * @return the number of samples
+ */
+ public int nSamples() {
+ return it.samples().nSamples();
+ }
+
+ /**
+ * Returns the number of VCF records in the overlap between the current
+ * window and the previous window. Returns 0 if the current window
+ * is the first window.
+ *
+ * @return the number of VCF records in the overlap between the current
+ * window and the previous window
+ */
+ public int overlap() {
+ return overlap;
+ }
+
+ /**
+ * Returns the number of distinct VCF records in the union of the current
+ * window and all previous windows.
+ *
+ * @return the number of distinct VCF records in the union of the current
+ * window and all previous windows
+ */
+ public int cumMarkerCnt() {
+ return cumMarkerCnt;
+ }
+
+ /**
+ * Releases any I/O resources controlled by this object.
+ */
+ @Override
+ public void close() {
+ it.close();
+ }
+
+ /**
+ * Returns a string representation of {@code this}. The exact
+ * details of the representation are unspecified and subject to change.
+ * @return a string representation of {@code this}
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(1100);
+ sb.append(this.getClass().toString());
+ sb.append("; next: ");
+ sb.append(next);
+ return sb.toString();
+ }
+}
diff --git a/vcf/VcfWriter.java b/vcf/VcfWriter.java
index 7df50e2..5c6fe53 100644
--- a/vcf/VcfWriter.java
+++ b/vcf/VcfWriter.java
@@ -26,6 +26,7 @@ import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import main.AlleleProbs;
+import main.ConstrainedAlleleProbs;
import main.GenotypeValues;
/**
@@ -43,18 +44,21 @@ public final class VcfWriter {
private static final DecimalFormat df2 = new DecimalFormat("#.##");
private static final DecimalFormat df3 = new DecimalFormat("#.###");
private static final DecimalFormat df2_fixed = new DecimalFormat("0.00");
- private static final MathContext mathContext2 = new MathContext(2);
+ private static final MathContext mc2 = new MathContext(2);
+ private static final BigDecimal ONE = new BigDecimal(1.0);
private static final String fileformat = "##fileformat=VCFv4.2";
private static final String afInfo = "##INFO=<ID=AF,Number=A,Type=Float,"
- + "Description=\"Estimated Allele Frequencies\">";
+ + "Description=\"Estimated ALT Allele Frequencies\">";
private static final String ar2Info = "##INFO=<ID=AR2,Number=1,Type=Float,"
+ "Description=\"Allelic R-Squared: estimated squared correlation between "
+ "most probable REF dose and true REF dose\">";
private static final String dr2Info = "##INFO=<ID=DR2,Number=1,Type=Float,"
+ "Description=\"Dosage R-Squared: estimated squared correlation between "
+ "estimated REF dose [P(RA) + 2*P(RR)] and true REF dose\">";
+ private static final String impInfo = "##INFO=<ID=IMP,Number=1,Type=Flag,"
+ + "Description=\"Imputed marker\">";
private static final String gtFormat = "##FORMAT=<ID=GT,Number=1,Type=String,"
+ "Description=\"Genotype\">";
@@ -134,6 +138,7 @@ public final class VcfWriter {
out.println(afInfo);
out.println(ar2Info);
out.println(dr2Info);
+ out.println(impInfo);
}
if (printGT) {
out.println(gtFormat);
@@ -238,8 +243,6 @@ public final class VcfWriter {
* @param alProbs the sample haplotype pairs
* @param start the starting marker index (inclusive)
* @param end the ending marker index (exclusive)
- * @param imputed {@code true} if there are imputed markers,
- * and {@code false} otherwise
* @param gprobs {@code true} if the GP field should be printed, and
* {@code false} otherwise.
* @param out the {@code PrintWriter} to which VCF records will
@@ -249,30 +252,68 @@ public final class VcfWriter {
* {@code (start < 0 || start > end || end > alProbs.nMarkers())}
* @throws NullPointerException if {@code haps == null || out == null}
*/
- public static void appendRecords(AlleleProbs alProbs, int start, int end,
- boolean imputed, boolean gprobs, PrintWriter out) {
+ public static void appendRecords(ConstrainedAlleleProbs alProbs,
+ int start, int end, boolean gprobs, PrintWriter out) {
if (start > end) {
throw new IllegalArgumentException("start=" + start + " end=" + end);
}
- for (int marker=start; marker<end; ++marker) {
- printFixedFields(alProbs, marker, imputed, gprobs, out);
+ boolean ds = true;
+ String format = format(ds, gprobs);
+ for (int m=start; m<end; ++m) {
+ Marker marker = alProbs.marker(m);
+ boolean isImputed = alProbs.isImputed(m);
+ GprobsStatistics gps = new GprobsStatistics(alProbs, m);
+ String info = info(gps, isImputed);
+ printFixedFields(marker, info, format, out);
for (int sample=0, n=alProbs.nSamples(); sample<n; ++sample) {
- printGTandDose(alProbs, marker, sample, imputed, out);
+ printGTandDose(alProbs, m, sample, ds, out);
if (gprobs) {
- printGP(alProbs, marker, sample, out);
+ printGP(alProbs, m, sample, out);
}
}
out.println();
}
}
+ /**
+ * Writes the specified genotype data as VCF records to the specified
+ * {@code PrintWriter}.
+ * @param alProbs the sample haplotype pairs
+ * @param start the starting marker index (inclusive)
+ * @param end the ending marker index (exclusive)
+ * @param out the {@code PrintWriter} to which VCF records will
+ * be written
+ *
+ * @throws IndexOutOfBoundsException if
+ * {@code (start < 0 || start > end || end > alProbs.nMarkers())}
+ * @throws NullPointerException if {@code haps == null || out == null}
+ */
+ public static void appendRecords(AlleleProbs alProbs, int start, int end,
+ PrintWriter out) {
+ boolean ds = false;
+ boolean gp = false;
+ String info = Const.MISSING_DATA_STRING;
+ String format = format(ds, gp);
+ if (start > end) {
+ throw new IllegalArgumentException("start=" + start + " end=" + end);
+ }
+ for (int m=start; m<end; ++m) {
+ Marker marker = alProbs.marker(m);
+ printFixedFields(marker, info, format, out);
+ for (int sample=0, n=alProbs.nSamples(); sample<n; ++sample) {
+ printGTandDose(alProbs, m, sample, ds, out);
+ }
+ out.println();
+ }
+ }
+
private static void printGTandDose(AlleleProbs alProbs, int marker, int
- sample, boolean imputed, PrintWriter out) {
+ sample, boolean ds, PrintWriter out) {
out.print(Const.tab);
out.print(alProbs.allele1(marker, sample));
out.append(Const.phasedSep);
out.print(alProbs.allele2(marker, sample));
- if (imputed) {
+ if (ds) {
int nAlleles = alProbs.marker(marker).nAlleles();
for (int j = 1; j < nAlleles; ++j) {
float p1 = alProbs.alProb1(marker, sample, j);
@@ -324,7 +365,7 @@ public final class VcfWriter {
private static void printFixedFields(GenotypeValues gv, int marker,
PrintWriter out) {
GprobsStatistics gpm = new GprobsStatistics(gv, marker);
- float[] alleleFreq = gpm.alleleFreq();
+ double[] alleleFreq = gpm.alleleFreq();
out.print(gv.marker(marker));
out.print(Const.tab);
out.print(Const.MISSING_DATA_CHAR); // QUAL
@@ -337,44 +378,65 @@ public final class VcfWriter {
out.print(df2_fixed.format(gpm.doseR2()));
for (int j=1; j<alleleFreq.length; ++j) {
out.print( (j==1) ? ";AF=" : Const.comma);
- BigDecimal bd = new BigDecimal(alleleFreq[j]).round(mathContext2);
- out.print(bd.doubleValue());
+ out.print(formatProb(alleleFreq[j]));
}
out.print(Const.tab);
out.print("GT:DS:GP");
}
- private static void printFixedFields(AlleleProbs alProbs,
- int marker, boolean printR2, boolean gprobs, PrintWriter out) {
- GprobsStatistics gpm = new GprobsStatistics(alProbs, marker);
- float[] alleleFreq = gpm.alleleFreq();
- out.print(alProbs.marker(marker));
- out.print(Const.tab);
- out.print(Const.MISSING_DATA_CHAR); // QUAL
- out.print(Const.tab);
- out.print(PASS); // FILTER
- if (printR2) {
- out.print(Const.tab);
- out.print("AR2="); // INFO
- out.print(df2_fixed.format(gpm.allelicR2()));
- out.print(";DR2=");
- out.print(df2_fixed.format(gpm.doseR2()));
- for (int j=1; j<alleleFreq.length; ++j) {
- out.print( (j==1) ? ";AF=" : Const.comma);
- BigDecimal bd = new BigDecimal(alleleFreq[j]).round(mathContext2);
- out.print(bd.doubleValue());
+ private static String info(GprobsStatistics gps, boolean isImputed) {
+ double[] alleleFreq = gps.alleleFreq();
+ StringBuilder sb = new StringBuilder(20);
+ sb.append("AR2="); // INFO
+ sb.append(df2_fixed.format(gps.allelicR2()));
+ sb.append(";DR2=");
+ sb.append(df2_fixed.format(gps.doseR2()));
+ for (int j=1; j<alleleFreq.length; ++j) {
+ sb.append( (j==1) ? ";AF=" : Const.comma);
+ sb.append(formatProb(alleleFreq[j]));
+ }
+ if (isImputed) {
+ sb.append(";IMP");
+ }
+ return sb.toString();
+ }
+
+ private static String format(boolean ds, boolean gp) {
+ if (ds) {
+ if (gp) {
+ return "GT:DS:GP";
+ }
+ else {
+ return "GT:DS";
}
}
else {
- out.print(Const.tab);
- out.print(Const.MISSING_DATA_CHAR);
+ return "GT";
}
+ }
+
+ private static void printFixedFields(Marker marker, String info,
+ String format, PrintWriter out) {
+ out.print(marker);
+ out.print(Const.tab);
+ out.print(Const.MISSING_DATA_CHAR); // QUAL
out.print(Const.tab);
- if (printR2) {
- out.print(gprobs ? "GT:DS:GP" : "GT:DS");
+ out.print(PASS); // FILTER
+ out.print(Const.tab);
+ out.print(info);
+ out.print(Const.tab);
+ out.print(format);
+ }
+
+ private static String formatProb(double d) {
+ if (d>=0 && d <= 0.5) {
+ return new BigDecimal(d).round(mc2).toPlainString();
+ }
+ else if (d <= 1.0) {
+ return new BigDecimal(d-1.0).round(mc2).add(ONE).toString();
}
else {
- out.print("GT");
+ throw new IllegalArgumentException(String.valueOf(d));
}
}
}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/beagle.git
More information about the debian-med-commit
mailing list