[med-svn] [beast2-mcmc] 01/04: Imported Upstream version 2.4.3+dfsg

Andreas Tille tille at debian.org
Tue Aug 30 06:53:56 UTC 2016


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository beast2-mcmc.

commit 37327ec1be32f3b05104ffc50df98951d0c49469
Author: Andreas Tille <tille at debian.org>
Date:   Tue Aug 30 08:30:51 2016 +0200

    Imported Upstream version 2.4.3+dfsg
---
 build.xml                                          |   4 +-
 release/common/README.txt                          |   4 +-
 release/common/VERSION HISTORY.txt                 |  27 +-
 src/beast/app/BEASTVersion.java                    |   2 +-
 src/beast/app/BEASTVersion2.java                   |   2 +-
 src/beast/app/BeastMCMC.java                       |   8 +-
 src/beast/app/DocMaker.java                        |   3 +-
 src/beast/app/beastapp/BeastLauncher.java          |   2 +-
 src/beast/app/beastapp/BeastMain.java              |   2 +
 src/beast/app/beauti/AlignmentImporter.java        |  54 +++
 src/beast/app/beauti/AlignmentListInputEditor.java |   8 +-
 src/beast/app/beauti/Beauti.java                   |  12 +-
 src/beast/app/beauti/BeautiAlignmentProvider.java  | 468 ++++++++++++---------
 src/beast/app/beauti/BeautiDoc.java                |   1 +
 src/beast/app/beauti/GuessPatternDialog.java       |   5 +-
 src/beast/app/beauti/JPackageDialog.java           | 166 +++++++-
 src/beast/app/beauti/JPackageRepositoryDialog.java |   2 +
 src/beast/app/beauti/MRCAPriorInputEditor.java     | 101 +++++
 src/beast/app/beauti/OperatorListInputEditor.java  |   3 +-
 .../beauti/ParametricDistributionInputEditor.java  |  19 +-
 src/beast/app/beauti/PriorInputEditor.java         |   4 +-
 src/beast/app/beauti/PriorListInputEditor.java     | 173 +++++---
 src/beast/app/beauti/PriorProvider.java            |  24 ++
 src/beast/app/beauti/TaxonSetDialog.java           |   6 +-
 src/beast/app/beauti/TaxonSetInputEditor.java      |   9 +-
 src/beast/app/beauti/TipDatesInputEditor.java      | 159 -------
 src/beast/app/draw/BEASTObjectInputEditor.java     |   3 +-
 src/beast/app/draw/DoubleListInputEditor.java      |   7 +-
 src/beast/app/draw/EnumInputEditor.java            |   4 +
 src/beast/app/draw/InputEditor.java                |   2 +-
 src/beast/app/draw/IntegerListInputEditor.java     |   3 +-
 src/beast/app/tools/LogCombiner.java               |   8 +-
 src/beast/app/treeannotator/TreeAnnotator.java     |   2 +
 src/beast/app/util/Utils.java                      |   2 +-
 src/beast/core/BEASTInterface.java                 |  38 +-
 src/beast/core/Citation.java                       |  18 +-
 src/beast/core/Logger.java                         |   2 +-
 src/beast/core/parameter/Parameter.java            |   2 +-
 src/beast/core/util/Sum.java                       |  32 +-
 src/beast/evolution/alignment/TaxonSet.java        |   4 +-
 src/beast/evolution/datatype/IntegerData.java      |   7 +
 .../likelihood/ThreadedTreeLikelihood.java         |   8 +-
 .../evolution/speciation/CalibratedYuleModel.java  |   1 +
 src/beast/evolution/tree/RandomTree.java           |  16 +-
 src/beast/evolution/tree/TraitSet.java             |   3 +-
 src/beast/evolution/tree/Tree.java                 |   1 +
 src/beast/evolution/tree/TreeHeightLogger.java     |   7 +-
 src/beast/evolution/tree/TreeStatLogger.java       |  80 ++++
 .../tree/coalescent/ExponentialGrowth.java         |   5 +-
 .../evolution/tree/coalescent/TreeIntervals.java   |  21 +-
 src/beast/math/distributions/Gamma.java            |  42 +-
 src/beast/math/distributions/MRCAPrior.java        |   2 +-
 src/beast/util/AddOnManager.java                   |  89 +++-
 src/beast/util/Package.java                        |  16 +-
 src/beast/util/TreeParser.java                     |  15 +-
 src/beast/util/XMLParser.java                      |  37 +-
 src/beast/util/XMLParserUtils.java                 |  19 +-
 src/beast/util/XMLProducer.java                    |  26 +-
 .../beast/app/beauti/BeautiRateTutorialTest.java   |   5 +-
 src/test/beast/core/BEASTInterfaceTest.java        |  16 +
 src/test/beast/core/util/SumTest.java              |  63 +++
 .../beast/evolution/datatype/IntegerDataTest.java  |  32 ++
 src/test/beast/evolution/tree/TraitSetTest.java    |  78 ++++
 src/test/beast/math/distributions/GammaTest.java   | 101 +++++
 .../beast/math/distributions/InvGammaTest.java     | 128 ++++++
 .../LogNormalDistributionModelTest.java            | 140 +++++-
 .../beast/math/distributions/MRCAPriorTest.java    |  19 +
 .../math/distributions/NormalDistributionTest.java | 132 ++++++
 68 files changed, 1954 insertions(+), 550 deletions(-)

diff --git a/build.xml b/build.xml
index 3c6a376..420fe49 100644
--- a/build.xml
+++ b/build.xml
@@ -242,8 +242,8 @@
 
 
     <!-- Release -->
-    <property name="version" value="2.4.2" />
-    <property name="version_number" value="2.4.0" />
+    <property name="version" value="2.4.3" />
+    <property name="version_number" value="2.4.3" />
     <property name="release_dir" value="release" />
     <property name="copyright" value="Beast 2 development team 2011-2016" />
 
diff --git a/release/common/README.txt b/release/common/README.txt
index dae7c51..9e7954b 100644
--- a/release/common/README.txt
+++ b/release/common/README.txt
@@ -1,7 +1,7 @@
-                    BEAST v2.4.2 2016
+                    BEAST v2.4.3 2016
                  Beast 2 development team 2011-2016
 
-Last updated: June 2016
+Last updated: August 2016
 
 Contents:
 1) INTRODUCTION
diff --git a/release/common/VERSION HISTORY.txt b/release/common/VERSION HISTORY.txt
index 19b6d1e..c795a5c 100644
--- a/release/common/VERSION HISTORY.txt	
+++ b/release/common/VERSION HISTORY.txt	
@@ -1,10 +1,33 @@
-                    BEAST v2.4.2 2016
+                    BEAST v2.4.3 2016
                  Beast 2 development team 2011-2016
 Version History
-Last updated: June 2016
+Last updated: August 2016
 
 All issues can be viewed at https://github.com/CompEvol/beast2/issues
 ================================================================================
+Version 2.4.3 August 2016
+	
+	BEAUti
+		Support for tip data sampling by setting 'tipsonly' in MRCA Priors
+		Allow packages to specify priors, e.g. multi-monophyletic constraints in BEASTLabs
+		Allow packages to specify file importers, which allows microsattelite support through the BEASTvntr package
+		Gamma distribution allows multiple parameterisations
+		Packages used now encoded in XML, for better error reporting of missing packages
+		Better looking on high-res screens
+		
+	Package Manager
+		Links to documentation available
+		Better layout
+		
+	BEAST
+		allow multiple citations per class
+		allow trait sets with unspecified dates
+		allow multiple arguments to Sum
+		improved error reporting
+		
+	TreeAnnotator fix for phylogeography in low-mem mode.
+	LogCombiner suppress duplicate '=' in tree output
+	
 Version 2.4.2 June 2016
      Applications are scalable, making them visible on high resolution screens
 
diff --git a/src/beast/app/BEASTVersion.java b/src/beast/app/BEASTVersion.java
index a8b8e03..44a8abd 100644
--- a/src/beast/app/BEASTVersion.java
+++ b/src/beast/app/BEASTVersion.java
@@ -19,7 +19,7 @@ public class BEASTVersion extends Version {
     /**
      * Version string: assumed to be in format x.x.x
      */
-    private static final String VERSION = "2.4.2";
+    private static final String VERSION = "2.4.3";
 
     private static final String DATE_STRING = "2002-2016";
 
diff --git a/src/beast/app/BEASTVersion2.java b/src/beast/app/BEASTVersion2.java
index 6e029ae..d91d2b6 100644
--- a/src/beast/app/BEASTVersion2.java
+++ b/src/beast/app/BEASTVersion2.java
@@ -9,7 +9,7 @@ public class BEASTVersion2 extends BEASTVersion {
     /**
      * Version string: assumed to be in format x.x.x
      */
-    private static final String VERSION = "2.4.2";
+    private static final String VERSION = "2.4.3";
 
     private static final String DATE_STRING = "2002-2016";
 
diff --git a/src/beast/app/BeastMCMC.java b/src/beast/app/BeastMCMC.java
index 974b38a..4497bfd 100644
--- a/src/beast/app/BeastMCMC.java
+++ b/src/beast/app/BeastMCMC.java
@@ -418,12 +418,13 @@ public class BeastMCMC {
             Box box = Box.createHorizontalBox();
             box.add(new JLabel("Beast XML File: "));
             m_fileEntry = new JTextField();
-            Dimension size = new Dimension(300, 20);
+            int fontsize = m_fileEntry.getFont().getSize();
+            Dimension size = new Dimension(300 * fontsize / 13, 20 * fontsize / 13);
             m_fileEntry.setMinimumSize(size);
             m_fileEntry.setPreferredSize(size);
             m_fileEntry.setSize(size);
             m_fileEntry.setToolTipText("Enter file name of Beast 2 XML file");
-            m_fileEntry.setMaximumSize(new Dimension(1024, 20));
+            m_fileEntry.setMaximumSize(new Dimension(1024 * fontsize / 13, 20 * fontsize / 13));
             box.add(m_fileEntry);
             //box.add(Box.createHorizontalGlue());
 
@@ -459,7 +460,8 @@ public class BeastMCMC {
             m_seedEntry.setPreferredSize(size);
             m_seedEntry.setSize(size);
             m_seedEntry.setToolTipText("Enter seed number used for initialising the random number generator");
-            m_seedEntry.setMaximumSize(new Dimension(1024, 20));
+            int fontsize = m_seedEntry.getFont().getSize();
+            m_seedEntry.setMaximumSize(new Dimension(1024 * fontsize / 13, 20 * fontsize / 13));
             box.add(m_seedEntry);
             box.add(Box.createHorizontalGlue());
             return box;
diff --git a/src/beast/app/DocMaker.java b/src/beast/app/DocMaker.java
index 8bded67..ed25980 100644
--- a/src/beast/app/DocMaker.java
+++ b/src/beast/app/DocMaker.java
@@ -423,8 +423,7 @@ public class DocMaker {
         buf.append("<p>" + m_descriptions.get(beastObjectName) + "</p>\n");
 
         // show citation (if any)
-        Citation citation = beastObject.getCitation();
-        if (citation != null) {
+        for (Citation citation : beastObject.getCitationList()) {
             buf.append("<h2>Reference:</h2><p>" + citation.value() + "</p>\n");
             if (citation.DOI().length() > 0) {
                 buf.append("<p><a href=\"http://dx.doi.org/" + citation.DOI() + "\">doi:" + citation.DOI() + "</a></p>\n");
diff --git a/src/beast/app/beastapp/BeastLauncher.java b/src/beast/app/beastapp/BeastLauncher.java
index 198c1f8..ef96819 100644
--- a/src/beast/app/beastapp/BeastLauncher.java
+++ b/src/beast/app/beastapp/BeastLauncher.java
@@ -29,7 +29,7 @@ import beast.app.util.Utils6;
  * remainder of BEAST can be compiled against Java 1.8
  * **/
 public class BeastLauncher {
-	private static String getVersion() {return "2.4.2";}
+	private static String getVersion() {return "2.4.3";}
 	private static String getMajorVersion() {return "2.4";}
 	
 	private static String pathDelimiter;
diff --git a/src/beast/app/beastapp/BeastMain.java b/src/beast/app/beastapp/BeastMain.java
index 20f4c58..7375ebd 100644
--- a/src/beast/app/beastapp/BeastMain.java
+++ b/src/beast/app/beastapp/BeastMain.java
@@ -640,6 +640,8 @@ public class BeastMain {
                 }
                 Log.info.println();
                 Log.info.println("BEAST has terminated with an error. Please select QUIT from the menu.");
+            } else {
+            	rte.printStackTrace();
             }
             // logger.severe will throw a RTE but we want to keep the console visible
         } catch (XMLParserException e) {
diff --git a/src/beast/app/beauti/AlignmentImporter.java b/src/beast/app/beauti/AlignmentImporter.java
new file mode 100644
index 0000000..70080e8
--- /dev/null
+++ b/src/beast/app/beauti/AlignmentImporter.java
@@ -0,0 +1,54 @@
+package beast.app.beauti;
+
+import java.io.File;
+import java.util.List;
+
+import beast.core.BEASTInterface;
+
+
+/** Interface for importing alignments from file that are recognisable 
+ * from for example its file extension (see canHandleFile()). BeautiAlignmentProvider 
+ * will find implementations in packages through introspection, and these are used 
+ * when a implementation is not available in the BEAST core.
+ **/
+public interface AlignmentImporter {
+
+	/** return list of file extensions 
+	 * that are supported by this importer
+	 * @return
+	 */
+	public String [] getFileExtensions();
+	
+	/** process single file
+	 * @param file
+	 * @return list of Alignments found in file, and calibrations 
+	 */
+	public List<BEASTInterface> loadFile(File file);
+	
+	/** check whether the file can be processed by this particular importer.
+	 * Often, the first line of a file contains information about the nature 
+	 * of the file (e.g. #NEXUS in nexus files, <beast version="2.0"... in BEAST 2 
+	 * files) that can tell a bit more than just the file extension. 
+	 * 
+	 * By default, it only checks whether the file extension matches any of the 
+	 * ones listed in getFileExtension().
+	 * 
+	 * return true if file can be processed by this importer.
+	 */
+	default public boolean canHandleFile(File file) {
+		String name = file.getName();
+		if (name.lastIndexOf('.') == -1) {
+			// this file has no file extension
+			return false;
+		}
+		
+		String extension = name.substring(name.lastIndexOf('.') + 1);
+		for (String s : getFileExtensions()) {
+			if (s.equals(extension)) {
+				return true;
+			}
+		}
+		
+		return false;
+	}
+}
diff --git a/src/beast/app/beauti/AlignmentListInputEditor.java b/src/beast/app/beauti/AlignmentListInputEditor.java
index c6d7f65..8d73a4f 100644
--- a/src/beast/app/beauti/AlignmentListInputEditor.java
+++ b/src/beast/app/beauti/AlignmentListInputEditor.java
@@ -27,6 +27,7 @@ import javax.swing.JScrollPane;
 import javax.swing.JTable;
 import javax.swing.JTextField;
 import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
 import javax.swing.UIManager;
 import javax.swing.border.Border;
 import javax.swing.event.CellEditorListener;
@@ -152,7 +153,12 @@ public class AlignmentListInputEditor extends ListInputEditor {
         new FileDrop(null, scrollPane, focusBorder, new FileDrop.Listener() {
             @Override
 			public void filesDropped(java.io.File[] files) {
-                addFiles(files);
+            	SwingUtilities.invokeLater(new Runnable() {
+					@Override
+					public void run() {
+		                addFiles(files);
+					}
+				});
             }   // end filesDropped
         }); // end FileDrop.Listener
 
diff --git a/src/beast/app/beauti/Beauti.java b/src/beast/app/beauti/Beauti.java
index bb3383a..16d58e8 100644
--- a/src/beast/app/beauti/Beauti.java
+++ b/src/beast/app/beauti/Beauti.java
@@ -208,12 +208,16 @@ public class Beauti extends JTabbedPane implements BeautiDocListener {
 
         @Override
 		public void actionPerformed(ActionEvent ae) {
-            if (!doc.getFileName().equals("")) {
-                if (doc.validateModel() != DOC_STATUS.DIRTY) {
+            DOC_STATUS docStatus = doc.validateModel();
+            if (docStatus != DOC_STATUS.DIRTY) {
+                if (docStatus == DOC_STATUS.NO_DOCUMENT)
                     JOptionPane.showMessageDialog(null,
                             "There is no data to save to file");
-                    return;
-                }
+
+                return;
+            }
+
+            if (!doc.getFileName().equals("")) {
                 saveFile(doc.getFileName());
                 // m_doc.isSaved();
             } else {
diff --git a/src/beast/app/beauti/BeautiAlignmentProvider.java b/src/beast/app/beauti/BeautiAlignmentProvider.java
index fb526e1..44681f8 100644
--- a/src/beast/app/beauti/BeautiAlignmentProvider.java
+++ b/src/beast/app/beauti/BeautiAlignmentProvider.java
@@ -36,6 +36,7 @@ import beast.evolution.alignment.FilteredAlignment;
 import beast.evolution.alignment.Sequence;
 import beast.evolution.datatype.DataType;
 import beast.math.distributions.MRCAPrior;
+import beast.util.AddOnManager;
 import beast.util.NexusParser;
 import beast.util.XMLParser;
 
@@ -43,7 +44,36 @@ import beast.util.XMLParser;
 
 @Description("Class for creating new alignments to be edited by AlignmentListInputEditor")
 public class BeautiAlignmentProvider extends BEASTObject {
-	
+	/** map extension to importer class names **/
+	static List<AlignmentImporter> importers = null;
+    /**
+     * directory to pick up importers from *
+     */
+    final static String[] IMPLEMENTATION_DIR = {"beast.app"};
+
+	private void initImporters() {
+		importers = new ArrayList<>();
+        // add standard importers
+		importers.add(new NexusImporter());
+		importers.add(new XMLImporter());
+       	importers.add(new FastaImporter());
+
+        // build up list of data types
+        List<String> importerClasses = AddOnManager.find(AlignmentImporter.class, IMPLEMENTATION_DIR);
+        for (String _class: importerClasses) {
+        	try {
+        		if (!_class.startsWith(this.getClass().getName())) {
+					AlignmentImporter importer = (AlignmentImporter) Class.forName(_class).newInstance();
+					importers.add(importer);
+        		}
+			} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+        }
+        
+	}
+
 	final public Input<BeautiSubTemplate> template = new Input<>("template", "template to be used after creating a new alignment. ", Validate.REQUIRED);
 	
 	@Override
@@ -62,10 +92,17 @@ public class BeautiAlignmentProvider extends BEASTObject {
 	 * return new alignment, return null if not successful 
 	 * **/
 	protected List<BEASTInterface> getAlignments(BeautiDoc doc) {
+		if (importers == null) {
+			initImporters();
+		}
+		Set<String> extensions = new HashSet<>();
+		for (AlignmentImporter importer : importers) {
+			for (String extension : importer.getFileExtensions()) {
+				extensions.add(extension);
+			}
+		}
         File [] files = beast.app.util.Utils.getLoadFiles("Load Alignment File",
-                new File(Beauti.g_sDir), "Alignment files", "xml", 
-                "fa","fas","fst","fasta","fna","ffn","faa","frn",
-                "nex","nxs","nexus");
+                new File(Beauti.g_sDir), "Alignment files", extensions.toArray(new String[]{}));
         if (files != null && files.length > 0) {
             return getAlignments(doc, files);
         }
@@ -79,100 +116,52 @@ public class BeautiAlignmentProvider extends BEASTObject {
      * @return
      */
     public List<BEASTInterface> getAlignments(BeautiDoc doc, File[] files) {
+		if (importers == null) {
+			initImporters();
+		}
         List<BEASTInterface> selectedBEASTObjects = new ArrayList<>();
         List<MRCAPrior> calibrations = new ArrayList<>();
         for (File file : files) {
-            String fileName = file.getName();
-			String fileExtension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
-			Alignment alignment;
-
-			switch (fileExtension) {
-				case ".nex":
-				case ".nxs":
-				case ".nexus":
-					NexusParser parser = new NexusParser();
-					try {
-						parser.parseFile(file);
-						if (parser.filteredAlignments.size() > 0) {
-							/**
-							 * sanity check: make sure the filters do not
-							 * overlap
-							 **/
-							int[] used = new int[parser.m_alignment.getSiteCount()];
-							Set<Integer> overlap = new HashSet<>();
-							int partitionNr = 1;
-							for (Alignment data : parser.filteredAlignments) {
-								int[] indices = ((FilteredAlignment) data).indices();
-								for (int i : indices) {
-									if (used[i] > 0) {
-										overlap.add(used[i] * 10000 + partitionNr);
-									} else {
-										used[i] = partitionNr;
-									}
-								}
-								partitionNr++;
-							}
-							if (overlap.size() > 0) {
-								String overlaps = "<html>Warning: The following partitions overlap:<br/>";
-								for (int i : overlap) {
-									overlaps += parser.filteredAlignments.get(i / 10000 - 1).getID()
-											+ " overlaps with "
-											+ parser.filteredAlignments.get(i % 10000 - 1).getID() + "<br/>";
-								}
-								overlaps += "The first thing you might want to do is delete some of these partitions.</html>";
-								JOptionPane.showMessageDialog(null, overlaps);
-							}
-							/** add alignments **/
-							for (Alignment data : parser.filteredAlignments) {
-								sortByTaxonName(data.sequenceInput.get());
-								selectedBEASTObjects.add(data);
-							}
-							if (parser.calibrations != null) {
-								if (calibrations == null) {
-									calibrations = new ArrayList<>();
-								}
-								calibrations.addAll(parser.calibrations);
-							}
-						} else {
-							selectedBEASTObjects.add(parser.m_alignment);
-							if (parser.calibrations != null) {
-								if (calibrations == null) {
-									calibrations = new ArrayList<>();
-								}
-								calibrations.addAll(parser.calibrations);
-							}
-						}
-					} catch (Exception ex) {
-						ex.printStackTrace();
-						JOptionPane.showMessageDialog(null, "Loading of " + fileName + " failed: " + ex.getMessage());
-						return null;
+			// create list of importers that can handle the file
+			List<AlignmentImporter> availableImporters = new ArrayList<>();
+			for (AlignmentImporter importer : importers) {
+				if (importer.canHandleFile(file)) {
+					availableImporters.add(importer);
+				}
+			}
+			
+			if (availableImporters.size() > 0) {
+				AlignmentImporter importer = availableImporters.get(0);
+				if (availableImporters.size() > 1) {
+					// let user choose an importer
+					List<String> descriptions = new ArrayList<>();
+					for (AlignmentImporter i : availableImporters) {
+						descriptions.add(((BEASTInterface)i).getDescription());
 					}
-					break;
-
-				case ".xml":
-					alignment = (Alignment)getXMLData(file);
-					selectedBEASTObjects.add(alignment);
-					break;
-
-				case ".fa":
-				case ".fas":
-				case ".fasta":
-				case ".fst":
-				case ".fna":
-				case ".ffn":
-				case ".faa":
-				case ".frn":
-					alignment = getFASTAData(file);
-					sortByTaxonName(alignment.sequenceInput.get());
-					selectedBEASTObjects.add(alignment);
-					break;
-
-                default:
-                    JOptionPane.showMessageDialog(null,
-                            "Unsupported sequence file extension.",
-                            "Error", JOptionPane.ERROR_MESSAGE);
-                    break;
+					String option = (String)JOptionPane.showInputDialog(null, "Which importer is appropriate", "Option",
+		                    JOptionPane.WARNING_MESSAGE, null, descriptions.toArray(), descriptions.get(0));
+					if (option == null) {
+						return selectedBEASTObjects;
+					}
+					int i = descriptions.indexOf(option);
+					importer = availableImporters.get(i);
+				}
+				
+				// get a fresh instance
+				try {
+					importer = importer.getClass().newInstance();
+				} catch (InstantiationException | IllegalAccessException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+				List<BEASTInterface> list = importer.loadFile(file);
+				selectedBEASTObjects.addAll(list);
+			} else {
+                JOptionPane.showMessageDialog(null,
+                        "Unsupported sequence file.",
+                        "Error", JOptionPane.ERROR_MESSAGE);
 			}
+			
         }
         addAlignments(doc, selectedBEASTObjects);
         if (calibrations != null) {
@@ -185,16 +174,18 @@ public class BeautiAlignmentProvider extends BEASTObject {
     
     protected void addAlignments(BeautiDoc doc, List<BEASTInterface> selectedBEASTObjects) {
         for (BEASTInterface beastObject : selectedBEASTObjects) {
-        	// ensure ID of alignment is unique
-        	int k = 0;
-        	String id = beastObject.getID();
-        	while (doc.pluginmap.containsKey(id)) {
-        		k++;
-        		id = beastObject.getID() + k;
+        	if (beastObject instanceof Alignment) {
+	        	// ensure ID of alignment is unique
+	        	int k = 0;
+	        	String id = beastObject.getID();
+	        	while (doc.pluginmap.containsKey(id)) {
+	        		k++;
+	        		id = beastObject.getID() + k;
+	        	}
+	        	beastObject.setID(id);
+	        	sortByTaxonName(((Alignment) beastObject).sequenceInput.get());
+	            doc.addAlignmentWithSubnet((Alignment) beastObject, getStartTemplate());
         	}
-        	beastObject.setID(id);
-        	sortByTaxonName(((Alignment) beastObject).sequenceInput.get());
-            doc.addAlignmentWithSubnet((Alignment) beastObject, getStartTemplate());
         }
     }
 
@@ -262,94 +253,6 @@ public class BeautiAlignmentProvider extends BEASTObject {
 		}
 	}
 	
-    private Alignment getFASTAData(File file) {
-    	try {
-    		// grab alignment data
-        	Map<String, StringBuilder> seqMap = new HashMap<>();
-        	List<String> taxa = new ArrayList<>();
-        	String currentTaxon = null;
-			BufferedReader fin = new BufferedReader(new FileReader(file));
-	        String missing = "?";
-	        String gap = "-";
-	        int totalCount = 4;
-	        String datatype = "nucleotide";
-	        // According to http://en.wikipedia.org/wiki/FASTA_format lists file formats and their data content
-			// .fna = nucleic acid
-			// .ffn = nucleotide coding regions
-			// .frn = non-coding RNA
-			// .ffa = amino acid
-    		boolean mayBeAminoacid = !(file.getName().toLowerCase().endsWith(".fna") || file.getName().toLowerCase().endsWith(".ffn") || file.getName().toLowerCase().endsWith(".frn"));
-    		
-			while (fin.ready()) {
-				String line = fin.readLine();
-				if (line.startsWith(";")) {
-					// it is a comment, ignore
-				} else 	if (line.startsWith(">")) {
-					// it is a taxon
-					currentTaxon = line.substring(1).trim();
-					// only up to first space
-					currentTaxon = currentTaxon.replaceAll("\\s.*$", "");
-				} else {
-					// it is a data line
-					if (currentTaxon == null) {
-						fin.close();
-						throw new RuntimeException("Expected taxon defined on first line");
-					}
-					if (seqMap.containsKey(currentTaxon)) {
-						StringBuilder sb = seqMap.get(currentTaxon);
-						sb.append(line);
-					} else {
-						StringBuilder sb = new StringBuilder();
-						seqMap.put(currentTaxon, sb);
-						sb.append(line);
-						taxa.add(currentTaxon);
-					}
-				}
-			}
-			fin.close();
-			
-			int charCount = -1;
-			Alignment alignment = new Alignment();
-	        for (final String taxon : taxa) {
-	            final StringBuilder bsData = seqMap.get(taxon);
-	            String data = bsData.toString();
-	            data = data.replaceAll("\\s", "");
-	            seqMap.put(taxon, new StringBuilder(data));
-
-	            if (charCount < 0) {charCount = data.length();}
-	            if (data.length() != charCount) {
-	                throw new IllegalArgumentException("Expected sequence of length " + charCount + " instead of " + data.length() + " for taxon " + taxon);
-	            }
-	            // map to standard missing and gap chars
-	            data = data.replace(missing.charAt(0), DataType.MISSING_CHAR);
-	            data = data.replace(gap.charAt(0), DataType.GAP_CHAR);
-
-	            if (mayBeAminoacid && datatype.equals("nucleotide") && !data.matches("[ACGTUXNacgtuxn?_-]+")) {
-	            	datatype = "aminoacid";
-	            	totalCount = 20;
-	            	for (Sequence seq : alignment.sequenceInput.get()) {
-	            		seq.totalCountInput.setValue(totalCount, seq);
-	            	}
-	            }
-	            
-	            final Sequence sequence = new Sequence();
-	            data = data.replaceAll("[Xx]", "?");
-	            sequence.init(totalCount, taxon, data);
-	            sequence.setID(NexusParser.generateSequenceID(taxon));
-	            alignment.sequenceInput.setValue(sequence, alignment);
-	        }
-	        String ID = file.getName();
-	        ID = ID.substring(0, ID.lastIndexOf('.')).replaceAll("\\..*", "");
-	        alignment.setID(ID);
-			alignment.dataTypeInput.setValue(datatype, alignment);
-	        alignment.initAndValidate();
-	        return alignment;
-    	} catch (Exception e) {
-			e.printStackTrace();
-			JOptionPane.showMessageDialog(null, "Loading of " + file.getName() + " failed: " + e.getMessage());
-    	}
-		return null;
-	}
 
 	private static BEASTInterface parseBeast1XML(String ID, String xml) throws SAXException, IOException, ParserConfigurationException  {
 		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
@@ -417,4 +320,189 @@ public class BeautiAlignmentProvider extends BEASTObject {
 		return null;
 	}
 
+	@Description("NEXUS file importer")
+	class NexusImporter implements AlignmentImporter {
+
+		@Override
+		public String[] getFileExtensions() {
+			return new String[]{"nex","nxs","nexus"};
+		}
+
+		@Override
+		public List<BEASTInterface> loadFile(File file) {
+			List<BEASTInterface> selectedBEASTObjects = new ArrayList<>();
+			NexusParser parser = new NexusParser();
+			try {
+				parser.parseFile(file);
+				if (parser.filteredAlignments.size() > 0) {
+					/**
+					 * sanity check: make sure the filters do not
+					 * overlap
+					 **/
+					int[] used = new int[parser.m_alignment.getSiteCount()];
+					Set<Integer> overlap = new HashSet<>();
+					int partitionNr = 1;
+					for (Alignment data : parser.filteredAlignments) {
+						int[] indices = ((FilteredAlignment) data).indices();
+						for (int i : indices) {
+							if (used[i] > 0) {
+								overlap.add(used[i] * 10000 + partitionNr);
+							} else {
+								used[i] = partitionNr;
+							}
+						}
+						partitionNr++;
+					}
+					if (overlap.size() > 0) {
+						String overlaps = "<html>Warning: The following partitions overlap:<br/>";
+						for (int i : overlap) {
+							overlaps += parser.filteredAlignments.get(i / 10000 - 1).getID()
+									+ " overlaps with "
+									+ parser.filteredAlignments.get(i % 10000 - 1).getID() + "<br/>";
+						}
+						overlaps += "The first thing you might want to do is delete some of these partitions.</html>";
+						JOptionPane.showMessageDialog(null, overlaps);
+					}
+					/** add alignments **/
+					for (Alignment data : parser.filteredAlignments) {
+						sortByTaxonName(data.sequenceInput.get());
+						selectedBEASTObjects.add(data);
+					}
+					if (parser.calibrations != null) {
+						selectedBEASTObjects.addAll(parser.calibrations);
+					}
+				} else {
+					selectedBEASTObjects.add(parser.m_alignment);
+					if (parser.calibrations != null) {
+						selectedBEASTObjects.addAll(parser.calibrations);
+					}
+				}
+			} catch (Exception ex) {
+				ex.printStackTrace();
+				JOptionPane.showMessageDialog(null, "Loading of " + file.getPath() + " failed: " + ex.getMessage());
+				return null;
+			}
+			return selectedBEASTObjects;
+		}
+	}
+	
+	@Description("BEAST XML file importer")
+	class XMLImporter implements AlignmentImporter {
+
+		@Override
+		public String[] getFileExtensions() {
+			return new String[]{"xml"};
+		}
+
+		@Override
+		public List<BEASTInterface> loadFile(File file) {
+			List<BEASTInterface> selectedBEASTObjects = new ArrayList<>();
+			Alignment alignment = (Alignment)getXMLData(file);
+			selectedBEASTObjects.add(alignment);
+			return selectedBEASTObjects;
+		}
+		
+	}
+
+	@Description("Fasta file importer")
+	class FastaImporter implements AlignmentImporter {
+
+		@Override
+		public String[] getFileExtensions() {
+			return new String[]{"fa","fas","fst","fasta","fna","ffn","faa","frn"};
+		}
+
+		@Override
+		public List<BEASTInterface> loadFile(File file) {
+			List<BEASTInterface> selectedBEASTObjects = new ArrayList<>();
+		    	try {
+		    		// grab alignment data
+		        	Map<String, StringBuilder> seqMap = new HashMap<>();
+		        	List<String> taxa = new ArrayList<>();
+		        	String currentTaxon = null;
+					BufferedReader fin = new BufferedReader(new FileReader(file));
+			        String missing = "?";
+			        String gap = "-";
+			        int totalCount = 4;
+			        String datatype = "nucleotide";
+			        // According to http://en.wikipedia.org/wiki/FASTA_format lists file formats and their data content
+					// .fna = nucleic acid
+					// .ffn = nucleotide coding regions
+					// .frn = non-coding RNA
+					// .ffa = amino acid
+		    		boolean mayBeAminoacid = !(file.getName().toLowerCase().endsWith(".fna") || file.getName().toLowerCase().endsWith(".ffn") || file.getName().toLowerCase().endsWith(".frn"));
+		    		
+					while (fin.ready()) {
+						String line = fin.readLine();
+						if (line.startsWith(";")) {
+							// it is a comment, ignore
+						} else 	if (line.startsWith(">")) {
+							// it is a taxon
+							currentTaxon = line.substring(1).trim();
+							// only up to first space
+							currentTaxon = currentTaxon.replaceAll("\\s.*$", "");
+						} else {
+							// it is a data line
+							if (currentTaxon == null) {
+								fin.close();
+								throw new RuntimeException("Expected taxon defined on first line");
+							}
+							if (seqMap.containsKey(currentTaxon)) {
+								StringBuilder sb = seqMap.get(currentTaxon);
+								sb.append(line);
+							} else {
+								StringBuilder sb = new StringBuilder();
+								seqMap.put(currentTaxon, sb);
+								sb.append(line);
+								taxa.add(currentTaxon);
+							}
+						}
+					}
+					fin.close();
+					
+					int charCount = -1;
+					Alignment alignment = new Alignment();
+			        for (final String taxon : taxa) {
+			            final StringBuilder bsData = seqMap.get(taxon);
+			            String data = bsData.toString();
+			            data = data.replaceAll("\\s", "");
+			            seqMap.put(taxon, new StringBuilder(data));
+
+			            if (charCount < 0) {charCount = data.length();}
+			            if (data.length() != charCount) {
+			                throw new IllegalArgumentException("Expected sequence of length " + charCount + " instead of " + data.length() + " for taxon " + taxon);
+			            }
+			            // map to standard missing and gap chars
+			            data = data.replace(missing.charAt(0), DataType.MISSING_CHAR);
+			            data = data.replace(gap.charAt(0), DataType.GAP_CHAR);
+
+			            if (mayBeAminoacid && datatype.equals("nucleotide") && !data.matches("[ACGTUXNacgtuxn?_-]+")) {
+			            	datatype = "aminoacid";
+			            	totalCount = 20;
+			            	for (Sequence seq : alignment.sequenceInput.get()) {
+			            		seq.totalCountInput.setValue(totalCount, seq);
+			            	}
+			            }
+			            
+			            final Sequence sequence = new Sequence();
+			            data = data.replaceAll("[Xx]", "?");
+			            sequence.init(totalCount, taxon, data);
+			            sequence.setID(NexusParser.generateSequenceID(taxon));
+			            alignment.sequenceInput.setValue(sequence, alignment);
+			        }
+			        String ID = file.getName();
+			        ID = ID.substring(0, ID.lastIndexOf('.')).replaceAll("\\..*", "");
+			        alignment.setID(ID);
+					alignment.dataTypeInput.setValue(datatype, alignment);
+			        alignment.initAndValidate();
+			        selectedBEASTObjects.add(alignment);
+		    	} catch (Exception e) {
+					e.printStackTrace();
+					JOptionPane.showMessageDialog(null, "Loading of " + file.getName() + " failed: " + e.getMessage());
+		    	}
+			return selectedBEASTObjects;
+		}
+		
+	}
+
 }
diff --git a/src/beast/app/beauti/BeautiDoc.java b/src/beast/app/beauti/BeautiDoc.java
index 2d92a4f..c1a6d86 100644
--- a/src/beast/app/beauti/BeautiDoc.java
+++ b/src/beast/app/beauti/BeautiDoc.java
@@ -2504,6 +2504,7 @@ public class BeautiDoc extends BEASTObject implements RequiredInputProvider {
 		        			t.taxonsetInput.get().get(0).getID() + "=" + date
 		        			, dateTrait);	
 		        }
+		        mrcaPrior.onlyUseTipsInput.setValue(true, mrcaPrior);
 			}
 	}
 
diff --git a/src/beast/app/beauti/GuessPatternDialog.java b/src/beast/app/beauti/GuessPatternDialog.java
index 88b61ac..4653762 100644
--- a/src/beast/app/beauti/GuessPatternDialog.java
+++ b/src/beast/app/beauti/GuessPatternDialog.java
@@ -134,7 +134,8 @@ public class GuessPatternDialog extends JDialog {
         textRegExp.setText(pattern);
         textRegExp.setColumns(10);
         textRegExp.setToolTipText("Enter regular expression to match taxa");
-        textRegExp.setMaximumSize(new Dimension(1024, 25));
+        int fontsize = textRegExp.getFont().getSize();
+        textRegExp.setMaximumSize(new Dimension(1024 * fontsize/13, 25 * fontsize/13));
         GridBagConstraints gbc2 = new GridBagConstraints();
         gbc2.insets = new Insets(0, 0, 5, 5);
         gbc2.anchor = GridBagConstraints.WEST;
@@ -559,7 +560,7 @@ public class GuessPatternDialog extends JDialog {
                 }
                 
                if (trait.trim().length() == 0) {
-            	   JOptionPane.showMessageDialog(m_parent, "Could not find traint information in the file. " +
+            	   JOptionPane.showMessageDialog(m_parent, "Could not find trait information in the file. " +
             			   "Perhaps this is not a tab-delimited but space file?");
                }
             } catch (Exception e) {
diff --git a/src/beast/app/beauti/JPackageDialog.java b/src/beast/app/beauti/JPackageDialog.java
index afcd5f7..c591e15 100644
--- a/src/beast/app/beauti/JPackageDialog.java
+++ b/src/beast/app/beauti/JPackageDialog.java
@@ -1,5 +1,7 @@
 package beast.app.beauti;
 
+import beast.app.util.Arguments;
+import beast.app.util.Utils;
 import beast.core.Description;
 import beast.util.AddOnManager;
 import beast.util.Package;
@@ -9,11 +11,16 @@ import javax.swing.*;
 import javax.swing.event.TableModelEvent;
 import javax.swing.table.AbstractTableModel;
 import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
 import javax.swing.table.TableModel;
 import java.awt.*;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionAdapter;
 import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
 import java.util.*;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -66,6 +73,7 @@ public class JPackageDialog extends JPanel {
         	@Override
 			public void run() {
                 resetPackages();
+                dataTable.updateWidths();
         		isRunning = false;
         	}
         };
@@ -113,39 +121,73 @@ public class JPackageDialog extends JPanel {
     private void createTable() {
         DataTableModel dataTableModel = new DataTableModel();
         dataTable = new PackageTable(dataTableModel);
-        
-        double [] widths = new double[dataTable.getColumnCount()];
-        //double total = 0;
-        for (int i = 0; i < dataTable.getColumnCount(); i++) {
-        	widths[i] = dataTable.getColumnModel().getColumn(i).getWidth();
-        	//total += widths[i]; 
-        }
-        widths[2] /= 4.0;
-        dataTable.getColumnModel().getColumn(2).setPreferredWidth((int) widths[2]);
-        dataTable.getColumnModel().getColumn(2).setMinWidth((int) widths[2]);
-        widths[3] /= 2.0; 
-        dataTable.getColumnModel().getColumn(3).setPreferredWidth((int) widths[3]);
-        widths[4] *= 2.0; 
-        dataTable.getColumnModel().getColumn(4).setPreferredWidth((int) widths[4]);
-        
-        
+        dataTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
+
         // TODO:
         // The following would work ...
         //dataTable.setAutoCreateRowSorter(true);
         // ...if all processing was done based on the data in the table, 
         // instead of the row number alone.
+
         dataTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
         dataTable.addMouseListener(new MouseAdapter() {
             @Override
-			public void mouseClicked(MouseEvent e) {
-                if (e.getClickCount() == 2) {
-                    Package selPackage = getSelectedPackage(dataTable.getSelectedRow());
-                    showDetail(selPackage);
+            public void mouseClicked(MouseEvent e) {
+                if (dataTable.getSelectedColumn() == dataTableModel.linkColumn) {
+                    URL url = getSelectedPackage(dataTable.getSelectedRow()).getProjectURL();
+                    if (url != null) {
+                        try {
+                            Desktop.getDesktop().browse(url.toURI());
+                        } catch (IOException | URISyntaxException e1) {
+                            e1.printStackTrace();
+                        }
+                    }
+
+                } else {
+                    if (e.getClickCount() == 2) {
+                        Package selPackage = getSelectedPackage(dataTable.getSelectedRow());
+                        showDetail(selPackage);
+                    }
                 }
             }
         });
+
+        dataTable.addMouseMotionListener(new MouseMotionAdapter() {
+            @Override
+            public void mouseMoved(MouseEvent e) {
+                super.mouseMoved(e);
+
+                int row = dataTable.rowAtPoint(e.getPoint());
+                int col = dataTable.columnAtPoint(e.getPoint());
+
+                int currentCursorType = dataTable.getCursor().getType();
+
+                if (col != dataTableModel.linkColumn) {
+                    if (currentCursorType == Cursor.HAND_CURSOR)
+                        dataTable.setCursor(Cursor.getDefaultCursor());
+
+                    return;
+                }
+
+                Package thisPkg = getSelectedPackage(row);
+
+                if (thisPkg.getProjectURL() == null) {
+                    if (currentCursorType == Cursor.HAND_CURSOR)
+                        dataTable.setCursor(Cursor.getDefaultCursor());
+
+                    return;
+                }
+
+                dataTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+
+            }
+        });
+
+		int size = dataTable.getFont().getSize();
+		dataTable.setRowHeight(20 * size/13);
     }
 
+
     private void resetPackages() {
         packageMap.clear();
         try {
@@ -351,7 +393,10 @@ public class JPackageDialog extends JPanel {
 	class DataTableModel extends AbstractTableModel {
 		private static final long serialVersionUID = 1L;
 
-		String[] columnNames = {"Name", "Status/Version", "Latest", "Dependencies", "Detail"};
+		String[] columnNames = {"Name", "Installed", "Latest", "Dependencies", "Link", "Detail"};
+
+        public final int linkColumn = 4;
+		ImageIcon linkIcon = Utils.getIcon(BeautiPanel.ICONPATH + "link.png");
 
         @Override
 		public int getColumnCount() {
@@ -370,12 +415,14 @@ public class JPackageDialog extends JPanel {
                 case 0:
                     return aPackage.getName();
                 case 1:
-                    return aPackage.getStatusString();
+                    return aPackage.getInstalledVersion();
                 case 2:
-                    return aPackage.isAvailable() ? aPackage.getLatestVersion() : "not available";
+                    return aPackage.getLatestVersion();
                 case 3:
                     return aPackage.getDependenciesString();
                 case 4:
+                    return aPackage.getProjectURL() != null ? linkIcon : null ;
+                case 5:
                     return aPackage.getDescription();
                 default:
                     throw new IllegalArgumentException("unknown column, " + col);
@@ -455,6 +502,14 @@ public class JPackageDialog extends JPanel {
         }
 
         @Override
+        public Class<?> getColumnClass(int column) {
+            if (column != ((DataTableModel)getModel()).linkColumn)
+                return String.class;
+            else
+                return ImageIcon.class;
+        }
+
+        @Override
         public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
             Component c =  super.prepareRenderer(renderer, row, column);
 
@@ -488,5 +543,70 @@ public class JPackageDialog extends JPanel {
 
             return c;
         }
+
+        /**
+         *  Calculate the width based on the widest cell renderer for the
+         *  given column.
+         *
+         * @param cIdx column index
+         * @return maximum width.
+         */
+        private int getColumnDataWidth(int cIdx)
+        {
+            int preferredWidth = 0;
+            int maxWidth = getColumnModel().getColumn(cIdx).getMaxWidth();
+
+            for (int row = 0; row < getRowCount(); row++)
+            {
+                preferredWidth = Math.max(preferredWidth, getCellDataWidth(row, cIdx));
+
+                //  We've exceeded the maximum width, no need to check other rows
+
+                if (preferredWidth >= maxWidth)
+                    break;
+            }
+
+            preferredWidth = Math.max(preferredWidth, getHeaderWidth(cIdx));
+
+            return preferredWidth;
+        }
+
+        /*
+         *  Get the preferred width for the specified cell
+         */
+        private int getCellDataWidth(int row, int column)
+        {
+            //  Inovke the renderer for the cell to calculate the preferred width
+
+            TableCellRenderer cellRenderer = getCellRenderer(row, column);
+            Component c = prepareRenderer(cellRenderer, row, column);
+
+            return c.getPreferredSize().width + 2*getIntercellSpacing().width;
+        }
+
+        /*
+         *  Get the preferred width for the specified header
+         */
+        private int getHeaderWidth(int cIdx)
+        {
+            //  Inovke the renderer for the cell to calculate the preferred width
+
+            TableColumn column = getColumnModel().getColumn(cIdx);
+            TableCellRenderer cellRenderer = getDefaultRenderer(String.class);
+            Component c = cellRenderer.getTableCellRendererComponent(this, column.getHeaderValue(), false, false, -1, cIdx);
+
+            return c.getPreferredSize().width + 2*getIntercellSpacing().width;
+        }
+
+
+        void updateWidths() {
+            for (int cIdx = 0; cIdx < getColumnCount(); cIdx++) {
+                int width = getColumnDataWidth(cIdx);
+
+                TableColumn column = getColumnModel().getColumn(cIdx);
+                getTableHeader().setResizingColumn(column);
+                column.setWidth(width);
+            }
+        }
     }
 }
diff --git a/src/beast/app/beauti/JPackageRepositoryDialog.java b/src/beast/app/beauti/JPackageRepositoryDialog.java
index 5373d57..1a5ee05 100644
--- a/src/beast/app/beauti/JPackageRepositoryDialog.java
+++ b/src/beast/app/beauti/JPackageRepositoryDialog.java
@@ -63,6 +63,8 @@ public class JPackageRepositoryDialog extends JDialog {
         // Assemble table
         final RepoTableModel repoTableModel = new RepoTableModel(urls);
         final JTable repoTable = new JTable(repoTableModel);
+		int size = repoTable.getFont().getSize();
+		repoTable.setRowHeight(20 * size/13);
         repoTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
         JScrollPane scrollPane = new JScrollPane(repoTable);
         getContentPane().add(scrollPane, BorderLayout.CENTER);
diff --git a/src/beast/app/beauti/MRCAPriorInputEditor.java b/src/beast/app/beauti/MRCAPriorInputEditor.java
index 35b12a0..18f0632 100644
--- a/src/beast/app/beauti/MRCAPriorInputEditor.java
+++ b/src/beast/app/beauti/MRCAPriorInputEditor.java
@@ -1,7 +1,10 @@
 package beast.app.beauti;
 
+import java.awt.Component;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -10,15 +13,19 @@ import javax.swing.Box;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
+import javax.swing.JOptionPane;
 
 import beast.app.draw.BEASTObjectPanel;
+import beast.app.draw.BooleanInputEditor;
 import beast.app.draw.InputEditor;
 import beast.app.draw.SmallButton;
 import beast.core.BEASTInterface;
 import beast.core.Input;
+import beast.core.Operator;
 import beast.core.util.Log;
 import beast.evolution.alignment.Taxon;
 import beast.evolution.alignment.TaxonSet;
+import beast.evolution.operators.TipDatesRandomWalker;
 import beast.evolution.tree.Tree;
 import beast.math.distributions.MRCAPrior;
 import beast.math.distributions.OneOnX;
@@ -58,9 +65,18 @@ public class MRCAPriorInputEditor extends InputEditor.Base {
                 MRCAPrior prior2 = (MRCAPrior) list.get(itemNr);
                 try {
                     TaxonSet taxonset = prior2.taxonsetInput.get();
+                    List<Taxon> originalTaxa = new ArrayList<>();
+                    originalTaxa.addAll(taxonset.taxonsetInput.get());
                     Set<Taxon> candidates = getTaxonCandidates(prior2);
                     TaxonSetDialog dlg = new TaxonSetDialog(taxonset, candidates, doc);
                     if (dlg.showDialog()) {
+        	            if (dlg.taxonSet.taxonsetInput.get().size() == 0) {
+        	            	JOptionPane.showMessageDialog(doc.beauti, "At least one taxon should be included in the taxon set",
+        	            			"Error specifying taxon set", JOptionPane.ERROR_MESSAGE);
+        	            	taxonset.taxonsetInput.get().addAll(originalTaxa);
+        	            	return;
+        	            }
+
                         prior2.taxonsetInput.setValue(dlg.taxonSet, prior2);
                         int i = 1;
                         String id = dlg.taxonSet.getID();
@@ -202,5 +218,90 @@ public class MRCAPriorInputEditor extends InputEditor.Base {
             }
         }
     }
+    
+    
+    InputEditor tipsonlyEditor;
+    
+    public InputEditor createTipsonlyEditor() throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+        BooleanInputEditor e = new BooleanInputEditor (doc) {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+        	public void init(Input<?> input, BEASTInterface beastObject, int itemNr, ExpandOption isExpandOption,
+        			boolean addButtons) {
+        		super.init(input, beastObject, itemNr, isExpandOption, addButtons);
+        		// hack to get to JCheckBox
+        		Component [] components = getComponents();       		
+        		((JCheckBox) components[0]).addActionListener(e -> {
+                	JCheckBox src = (JCheckBox) e.getSource();
+                	if (src.isSelected()) {
+                		enableTipSampling();
+                	} else {
+                		disableTipSampling();
+                	}
+                });
+        	}
+        	
+        };
+
+        MRCAPrior prior = (MRCAPrior) m_beastObject;
+        Input<?> input = prior.onlyUseTipsInput;
+        e.init(input, prior, -1, ExpandOption.FALSE, false);
+        return e;
+    }
+
+    // add TipDatesRandomWalker (if not present) and add to list of operators
+    private void enableTipSampling() {
+    	// First, create/find the operator
+    	TipDatesRandomWalker operator = null;
+    	MRCAPrior prior = (MRCAPrior) m_beastObject;
+    	TaxonSet taxonset = prior.taxonsetInput.get();
+    	taxonset.initAndValidate();
+    	
+    	// see if an old operator still hangs around -- happens when toggling the TipsOnly checkbox a few times
+    	for (BEASTInterface o : taxonset.getOutputs()) {
+    		if (o instanceof TipDatesRandomWalker) {
+    			operator = (TipDatesRandomWalker) o;
+    		}
+    	}
+    	
+    	if (operator == null) {
+    		operator = new TipDatesRandomWalker();
+    		operator.initByName("tree", prior.treeInput.get(), "taxonset", taxonset, "windowSize", 1.0, "weight", 1.0);
+    	}
+   		operator.setID("tipDatesSampler." + taxonset.getID());
+   	    	
+    	doc.mcmc.get().setInputValue("operator", operator);
+	}
+
+    // remove TipDatesRandomWalker from list of operators
+	private void disableTipSampling() {
+    	// First, find the operator
+    	TipDatesRandomWalker operator = null;
+    	MRCAPrior prior = (MRCAPrior) m_beastObject;
+    	TaxonSet taxonset = prior.taxonsetInput.get();
+    	
+    	// We cannot rely on the operator ID created in enableTipSampling()
+    	// since the taxoneset name may have changed.
+    	// However, if there is an TipDatesRandomWalker with taxonset as input, we want to remove it.
+    	for (BEASTInterface o : taxonset.getOutputs()) {
+    		if (o instanceof TipDatesRandomWalker) {
+    			operator = (TipDatesRandomWalker) o;
+    		}
+    	}
+    	
+    	if (operator == null) {
+    		// should never happen
+    		return;
+    	}
+    	
+    	// remove from list of operators
+    	Object o = doc.mcmc.get().getInput("operator");
+    	if (o instanceof Input<?>) {
+    		Input<List<Operator>> operatorInput = (Input<List<Operator>>) o;
+    		List<Operator> operators = operatorInput.get();
+    		operators.remove(operator);
+    	}
+	}
 
 }
diff --git a/src/beast/app/beauti/OperatorListInputEditor.java b/src/beast/app/beauti/OperatorListInputEditor.java
index a490b6a..241fcf7 100644
--- a/src/beast/app/beauti/OperatorListInputEditor.java
+++ b/src/beast/app/beauti/OperatorListInputEditor.java
@@ -81,7 +81,8 @@ public class OperatorListInputEditor extends ListInputEditor {
         Dimension size = new Dimension(50, 25);
         weightEntry.setMinimumSize(size);
         weightEntry.setPreferredSize(size);
-        weightEntry.setMaximumSize(new Dimension(50, 50));
+        int fontsize = weightEntry.getFont().getSize();
+        weightEntry.setMaximumSize(new Dimension(50 * fontsize/13, 50 * fontsize/13));
         itemBox.add(weightEntry);
 
         return this;
diff --git a/src/beast/app/beauti/ParametricDistributionInputEditor.java b/src/beast/app/beauti/ParametricDistributionInputEditor.java
index 9976d49..611a85a 100644
--- a/src/beast/app/beauti/ParametricDistributionInputEditor.java
+++ b/src/beast/app/beauti/ParametricDistributionInputEditor.java
@@ -119,7 +119,6 @@ public class ParametricDistributionInputEditor extends BEASTObjectInputEditor {
                 // ignore
             }
 
-            Font font = g.getFont();
             double minValue = 0.1;
             double maxValue = 1;
             try {
@@ -188,6 +187,7 @@ public class ParametricDistributionInputEditor extends BEASTObjectInputEditor {
             final int NR_OF_TICKS_Y = m_nTicks;
 
             // draw ticks on edge
+            Font font = g.getFont();
             Font smallFont = new Font(font.getName(), font.getStyle(), font.getSize() * 2/3);
             g.setFont(smallFont);
 
@@ -248,7 +248,8 @@ public class ParametricDistributionInputEditor extends BEASTObjectInputEditor {
                 g.drawString(ylabels[i], leftMargin - TICK_LENGTH - 1 - sfm.stringWidth(ylabels[i]), y + 3);
             }
 
-            g.setFont(new Font(font.getName(), font.getStyle(), font.getSize() * 10 / 12));
+            int fontHeight = font.getSize() * 10 / 12;
+            g.setFont(new Font(font.getName(), font.getStyle(), fontHeight));
             try {
                 FontMetrics fontMetrics = g.getFontMetrics();
                 String[] strs = new String[]{"2.5% Quantile", "5% Quantile", "Median", "95% Quantile", "97.5% Quantile"};
@@ -256,23 +257,23 @@ public class ParametricDistributionInputEditor extends BEASTObjectInputEditor {
             	mayBeUnstable = false;
                 for (k = 0; k < 5; k++) {
 
-                    int y = TOP_MARGIN + graphHeight + bottomMargin + g.getFontMetrics().getMaxAscent() + k * 10;
+                    int y = TOP_MARGIN + graphHeight + bottomMargin + g.getFontMetrics().getMaxAscent() + k * fontHeight;
 
                 	try {
                         g.drawString(format(m_distr.inverseCumulativeProbability(quantiles[k])), graphWidth / 2 + leftMargin, y);
                     } catch (MathException e) {
                         g.drawString("not available", graphWidth / 2 + leftMargin, y);
                     }
-                    g.drawString(strs[k], graphWidth / 2 - fontMetrics.stringWidth(strs[k]) + leftMargin - 10, y);
+                    g.drawString(strs[k], graphWidth / 2 - fontMetrics.stringWidth(strs[k]) + leftMargin - fontHeight, y);
                 }
                 if (mayBeUnstable) {
-                	int x = graphWidth * 3/ 4 + leftMargin; int y =TOP_MARGIN + graphHeight + bottomMargin + 10;
-                    g.drawString("* numbers", x, y + 20); 
-                    g.drawString("may not be", x, y + 30);                	
-                    g.drawString("accurate", x, y + 40);                	
+                	int x = graphWidth * 3/ 4 + leftMargin; int y =TOP_MARGIN + graphHeight + bottomMargin + fontHeight;
+                    g.drawString("* numbers", x, y + 2*fontHeight); 
+                    g.drawString("may not be", x, y + 3*fontHeight);                	
+                    g.drawString("accurate", x, y + 4*fontHeight);                	
                 }
                 try {
-                	g.drawString("mean " + format(m_distr.getMean()), graphWidth * 3/ 4 + leftMargin, TOP_MARGIN + graphHeight + bottomMargin + 10);
+                	g.drawString("mean " + format(m_distr.getMean()), graphWidth * 3/ 4 + leftMargin, TOP_MARGIN + graphHeight + bottomMargin + fontHeight);
                 } catch (RuntimeException e) {
                 	// catch in case it is not implemented.
                 }
diff --git a/src/beast/app/beauti/PriorInputEditor.java b/src/beast/app/beauti/PriorInputEditor.java
index 9d9060a..bee8056 100644
--- a/src/beast/app/beauti/PriorInputEditor.java
+++ b/src/beast/app/beauti/PriorInputEditor.java
@@ -10,6 +10,7 @@ import javax.swing.JButton;
 import javax.swing.JComboBox;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
+import javax.swing.text.StyledEditorKit.FontSizeAction;
 
 import beast.app.draw.BEASTObjectDialog;
 import beast.app.draw.InputEditor;
@@ -125,7 +126,8 @@ public class PriorInputEditor extends InputEditor.Base {
             itemBox.add(Box.createHorizontalStrut(10));
             itemBox.add(rangeButton);
         }
-        comboBox.setMaximumSize(new Dimension(1024, 24));
+        int fontsize = comboBox.getFont().getSize();
+        comboBox.setMaximumSize(new Dimension(1024 * fontsize / 13, 24 * fontsize / 13));
 
         String tipText = getDoc().tipTextMap.get(beastObject.getID());
         //System.out.println(beastObject.getID());
diff --git a/src/beast/app/beauti/PriorListInputEditor.java b/src/beast/app/beauti/PriorListInputEditor.java
index 48aa72c..c8e5a53 100644
--- a/src/beast/app/beauti/PriorListInputEditor.java
+++ b/src/beast/app/beauti/PriorListInputEditor.java
@@ -36,6 +36,7 @@ import beast.evolution.tree.TreeInterface;
 import beast.math.distributions.MRCAPrior;
 import beast.math.distributions.OneOnX;
 import beast.math.distributions.Prior;
+import beast.util.AddOnManager;
 
 
 
@@ -216,63 +217,131 @@ public class PriorListInputEditor extends ListInputEditor {
         sync();
         refreshPanel();
     } // addItem
+    
+    List<PriorProvider> priorProviders;
+    
+    private void initProviders() {
+    	priorProviders = new ArrayList<>();
+    	priorProviders.add(new MRCAPriorProvider());
+    	
+        // build up list of data types
+        List<String> importerClasses = AddOnManager.find(PriorProvider.class, new String[]{"beast.app"});
+        for (String _class: importerClasses) {
+        	try {
+        		if (!_class.startsWith(this.getClass().getName())) {
+        			PriorProvider priorProvider = (PriorProvider) Class.forName(_class).newInstance();
+					priorProviders.add(priorProvider);
+        		}
+			} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+        }
 
+    }
+    
     @Override
 	protected List<BEASTInterface> pluginSelector(Input<?> input, BEASTInterface parent, List<String> tabooList) {
-        MRCAPrior prior = new MRCAPrior();
-        try {
-
-            List<Tree> trees = new ArrayList<>();
-            getDoc().scrubAll(true, false);
-            State state = (State) doc.pluginmap.get("state");
-            for (StateNode node : state.stateNodeInput.get()) {
-                if (node instanceof Tree) { // && ((Tree) node).m_initial.get() != null) {
-                    trees.add((Tree) node);
-                }
-            }
-            int treeIndex = 0;
-            if (trees.size() > 1) {
-                String[] treeIDs = new String[trees.size()];
-                for (int j = 0; j < treeIDs.length; j++) {
-                    treeIDs[j] = trees.get(j).getID();
-                }
-                treeIndex = JOptionPane.showOptionDialog(null, "Select a tree", "MRCA selector",
-                        JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null,
-                        treeIDs, trees.get(0));
-            }
-            if (treeIndex < 0) {
-                return null;
-            }
-            prior.treeInput.setValue(trees.get(treeIndex), prior);
-            TaxonSet taxonSet = new TaxonSet();
-
-            TaxonSetDialog dlg = new TaxonSetDialog(taxonSet, getTaxonCandidates(prior), doc);
-            if (!dlg.showDialog() || dlg.taxonSet.getID() == null || dlg.taxonSet.getID().trim().equals("")) {
-                return null;
-            }
-            taxonSet = dlg.taxonSet;
-            int i = 1;
-            String id = taxonSet.getID();
-            while (doc.pluginmap.containsKey(taxonSet.getID()) && doc.pluginmap.get(taxonSet.getID()) != taxonSet) {
-            	taxonSet.setID(id + i);
-            	i++;
-            }
-            BEASTObjectPanel.addPluginToMap(taxonSet, doc);
-            prior.taxonsetInput.setValue(taxonSet, prior);
-            prior.setID(taxonSet.getID() + ".prior");
-            // this sets up the type
-            prior.distInput.setValue(new OneOnX(), prior);
-            // this removes the parametric distribution
-            prior.distInput.setValue(null, prior);
+    	if (priorProviders == null) {
+    		initProviders();
+    	}
+    	PriorProvider priorProvider = priorProviders.get(0);
+    	if (priorProviders.size() > 1) {
+			// let user choose a PriorProvider
+			List<String> descriptions = new ArrayList<>();
+			for (PriorProvider i : priorProviders) {
+				descriptions.add(i.getDescription());
+			}
+			String option = (String)JOptionPane.showInputDialog(null, "Which prior do you want to add", "Option",
+                    JOptionPane.WARNING_MESSAGE, null, descriptions.toArray(), descriptions.get(0));
+			if (option == null) {
+				return null;
+			}
+			int i = descriptions.indexOf(option);
+			priorProvider = priorProviders.get(i);
 
-            Logger logger = (Logger) doc.pluginmap.get("tracelog");
-            logger.loggersInput.setValue(prior, logger);
-        } catch (Exception e) {
-            // TODO: handle exception
-        }
+    	}
+    	
         List<BEASTInterface> selectedPlugins = new ArrayList<>();
-        selectedPlugins.add(prior);
-        g_collapsedIDs.add(prior.getID());
+        List<Distribution> distrs = priorProvider.createDistribution(doc);
+        if (distrs == null) {
+        	return null;
+        }
+        for (Distribution distr : distrs) {
+        	selectedPlugins.add(distr);
+        }
         return selectedPlugins;
     }
+    
+    class MRCAPriorProvider implements PriorProvider {
+    	@Override
+    	public List<Distribution> createDistribution(BeautiDoc doc) {
+	    	MRCAPrior prior = new MRCAPrior();
+	        try {
+	
+	            List<Tree> trees = new ArrayList<>();
+	            getDoc().scrubAll(true, false);
+	            State state = (State) doc.pluginmap.get("state");
+	            for (StateNode node : state.stateNodeInput.get()) {
+	                if (node instanceof Tree) { // && ((Tree) node).m_initial.get() != null) {
+	                    trees.add((Tree) node);
+	                }
+	            }
+	            int treeIndex = 0;
+	            if (trees.size() > 1) {
+	                String[] treeIDs = new String[trees.size()];
+	                for (int j = 0; j < treeIDs.length; j++) {
+	                    treeIDs[j] = trees.get(j).getID();
+	                }
+	                treeIndex = JOptionPane.showOptionDialog(null, "Select a tree", "MRCA selector",
+	                        JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null,
+	                        treeIDs, trees.get(0));
+	            }
+	            if (treeIndex < 0) {
+	                return null;
+	            }
+	            prior.treeInput.setValue(trees.get(treeIndex), prior);
+	            TaxonSet taxonSet = new TaxonSet();
+	
+	            TaxonSetDialog dlg = new TaxonSetDialog(taxonSet, getTaxonCandidates(prior), doc);
+	            if (!dlg.showDialog() || dlg.taxonSet.getID() == null || dlg.taxonSet.getID().trim().equals("")) {
+	                return null;
+	            }
+	            taxonSet = dlg.taxonSet;
+	            if (taxonSet.taxonsetInput.get().size() == 0) {
+	            	JOptionPane.showMessageDialog(doc.beauti, "At least one taxon should be included in the taxon set",
+	            			"Error specifying taxon set", JOptionPane.ERROR_MESSAGE);
+	            	return null;
+	            }
+	            int i = 1;
+	            String id = taxonSet.getID();
+	            while (doc.pluginmap.containsKey(taxonSet.getID()) && doc.pluginmap.get(taxonSet.getID()) != taxonSet) {
+	            	taxonSet.setID(id + i);
+	            	i++;
+	            }
+	            BEASTObjectPanel.addPluginToMap(taxonSet, doc);
+	            prior.taxonsetInput.setValue(taxonSet, prior);
+	            prior.setID(taxonSet.getID() + ".prior");
+	            // this sets up the type
+	            prior.distInput.setValue(new OneOnX(), prior);
+	            // this removes the parametric distribution
+	            prior.distInput.setValue(null, prior);
+	
+	            Logger logger = (Logger) doc.pluginmap.get("tracelog");
+	            logger.loggersInput.setValue(prior, logger);
+	        } catch (Exception e) {
+	            // TODO: handle exception
+	        }
+	        List<Distribution> selectedPlugins = new ArrayList<>();
+	        selectedPlugins.add(prior);
+	        g_collapsedIDs.add(prior.getID());
+	        return selectedPlugins;
+	    }
+
+		@Override
+		public String getDescription() {
+			return "MRCA prior";
+		}
+    	
+    }
 }
diff --git a/src/beast/app/beauti/PriorProvider.java b/src/beast/app/beauti/PriorProvider.java
new file mode 100644
index 0000000..39fbd26
--- /dev/null
+++ b/src/beast/app/beauti/PriorProvider.java
@@ -0,0 +1,24 @@
+package beast.app.beauti;
+
+import java.util.List;
+
+import beast.core.Distribution;
+
+/** packages can implement a PriorProvider. The PrioListInputEditor will
+ * pick up these PriorProviders by introspection. When a user selects the +
+ * button, the user can check whichever PriorProvider to add a new Distribution
+ * to the list of priors.
+ */
+public interface PriorProvider {
+	
+	/** create a distribution, but do not add to the prior -- this is handled
+	 * by the PrioListInputEditor. If null is returned, the operator is canceled.
+	 * @param doc useful to get information about the model being edited
+	 * @return Distribution to be added to prior, or null if nothing should 
+	 * be done.
+	 */
+	public List<Distribution> createDistribution(BeautiDoc doc);
+	
+	/** return description to be used in drop-down box for selecting among PriorProviders **/
+	public String getDescription();
+}
diff --git a/src/beast/app/beauti/TaxonSetDialog.java b/src/beast/app/beauti/TaxonSetDialog.java
index 2b72d14..7f993b4 100644
--- a/src/beast/app/beauti/TaxonSetDialog.java
+++ b/src/beast/app/beauti/TaxonSetDialog.java
@@ -137,7 +137,8 @@ public class TaxonSetDialog extends JDialog {
         //filterEntry.setPreferredSize(size);
         //filterEntry.setSize(size);
         filterEntry.setToolTipText("Enter regular expression to match taxa");
-        filterEntry.setMaximumSize(new Dimension(1024, 50));
+        int fontsize = filterEntry.getFont().getSize();
+        filterEntry.setMaximumSize(new Dimension(1024 * fontsize / 13, 50 * fontsize / 13));
         box.add(filterEntry);
         box.add(Box.createHorizontalGlue());
 
@@ -198,7 +199,8 @@ public class TaxonSetDialog extends JDialog {
             }
         });
 
-        box.setMaximumSize(new Dimension(400, 100));
+        int fontsize = idEntry.getFont().getSize();
+        box.setMaximumSize(new Dimension(400 * fontsize / 13, 100 * fontsize / 13));
         return box;
     }
     
diff --git a/src/beast/app/beauti/TaxonSetInputEditor.java b/src/beast/app/beauti/TaxonSetInputEditor.java
index 9b06762..31d4901 100644
--- a/src/beast/app/beauti/TaxonSetInputEditor.java
+++ b/src/beast/app/beauti/TaxonSetInputEditor.java
@@ -209,8 +209,10 @@ public class TaxonSetInputEditor extends InputEditor.Base {
         });
         m_table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
         m_table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
-        m_table.getColumnModel().getColumn(0).setPreferredWidth(250);
-        m_table.getColumnModel().getColumn(1).setPreferredWidth(250);
+		int size = m_table.getFont().getSize();
+		m_table.setRowHeight(20 * size/13);
+        m_table.getColumnModel().getColumn(0).setPreferredWidth(250 * size/13);
+        m_table.getColumnModel().getColumn(1).setPreferredWidth(250 * size/13);
 
         JTableHeader header = m_table.getTableHeader();
         header.addMouseListener(new ColumnHeaderListener());
@@ -474,7 +476,8 @@ public class TaxonSetInputEditor extends InputEditor.Base {
         // filterEntry.setPreferredSize(size);
         // filterEntry.setSize(size);
         filterEntry.setToolTipText("Enter regular expression to match taxa");
-        filterEntry.setMaximumSize(new Dimension(1024, 20));
+		int size = filterEntry.getFont().getSize();
+        filterEntry.setMaximumSize(new Dimension(1024, 20 * size/13));
         filterBox.add(filterEntry);
         filterBox.add(Box.createHorizontalGlue());
         filterEntry.getDocument().addDocumentListener(new DocumentListener() {
diff --git a/src/beast/app/beauti/TipDatesInputEditor.java b/src/beast/app/beauti/TipDatesInputEditor.java
index 55d638e..e3f653e 100644
--- a/src/beast/app/beauti/TipDatesInputEditor.java
+++ b/src/beast/app/beauti/TipDatesInputEditor.java
@@ -1,9 +1,7 @@
 package beast.app.beauti;
 
 import java.awt.*;
-import java.awt.event.ActionEvent;
 import java.text.DateFormat;
-import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.EventObject;
@@ -27,8 +25,6 @@ import beast.core.BEASTInterface;
 import beast.core.Input;
 import beast.core.util.Log;
 import beast.evolution.alignment.Taxon;
-import beast.evolution.alignment.TaxonSet;
-import beast.evolution.operators.TipDatesRandomWalker;
 import beast.evolution.tree.TraitSet;
 import beast.evolution.tree.Tree;
 
@@ -108,165 +104,10 @@ public class TipDatesInputEditor extends BEASTObjectInputEditor {
             if (traitSet != null) {
                 box.add(createButtonBox());
                 box.add(createListBox());
-                box.add(createSamplingBox());
             }
             add(box);
         }
     } // init
-    final static int NO_TIP_SAMPLING = 0;
-    final static int SAMPLE_TIPS_SAME_PRIOR = 1;
-    final static int SAMPLE_TIPS_MULTIPLE_PRIOR = 2;
-    final static String ALL_TAXA = "all";
-    int m_iMode = NO_TIP_SAMPLING;
-
-    private Component createSamplingBox() {
-        Box samplingBox = Box.createHorizontalBox();
-        JComboBox<String> comboBox = new JComboBox<>(new String[]{"no tips sampling", "sample tips from taxon set:"});// ,"sample tips with individual priors"});
-
-        comboBox.setMaximumSize(new Dimension(Integer.MAX_VALUE, comboBox.getPreferredSize().height));
-
-        // determine mode
-        m_iMode = NO_TIP_SAMPLING;
-        // count nr of TipDateScalers with weight > 0
-        String treeID = tree.getID();
-        String operatorID = "allTipDatesRandomWalker.t:" + treeID.substring(treeID.lastIndexOf(":") + 1);
-        TipDatesRandomWalker operator = (TipDatesRandomWalker) doc.pluginmap.get(operatorID);
-        if (operator != null && operator.m_pWeight.get() > 0) {
-            m_iMode = SAMPLE_TIPS_SAME_PRIOR;
-        }
-
-        m_iMode = Math.min(m_iMode, 2);
-        comboBox.setSelectedIndex(m_iMode);
-        comboBox.addActionListener(this::selectMode);
-        samplingBox.add(comboBox);
-
-        taxonsets = new ArrayList<>();
-        Taxon allTaxa = getDoc().getTaxon(ALL_TAXA);
-        allTaxa.setID(ALL_TAXA);
-        taxonsets.add(allTaxa);
-        List<String> taxonSetIDs = new ArrayList<>();
-        taxonSetIDs.add(ALL_TAXA);
-        for (Taxon taxon : doc.taxaset.values()) {
-            if (taxon instanceof TaxonSet) {
-                taxonsets.add(taxon);
-                taxonSetIDs.add(taxon.getID());
-            }
-        }
-        JComboBox<String> comboBox2 = new JComboBox<>(taxonSetIDs.toArray(new String[]{}));
-
-        comboBox2.setMaximumSize(new Dimension(Integer.MAX_VALUE, comboBox2.getPreferredSize().height));
-        
-        if (operator == null) {
-        	comboBox.setEnabled(false);
-        	comboBox2.setEnabled(false);
-        } else {
-	        // find TipDatesSampler and set TaxonSet input
-	        Taxon set = operator.m_taxonsetInput.get();
-	        if (set != null) {
-	            int i = taxonSetIDs.indexOf(set.getID());
-	            comboBox2.setSelectedIndex(i);
-	        }
-	
-	        comboBox2.addActionListener(this::selectTaxonSet);
-        }
-        samplingBox.add(comboBox2);
-
-        return samplingBox;
-    }
-
-    private void selectTaxonSet(ActionEvent e) {
-        @SuppressWarnings("unchecked")
-		JComboBox<String> comboBox = (JComboBox<String>) e.getSource();
-        String taxonSetID = (String) comboBox.getSelectedItem();
-        Taxon taxonset = null;;
-        for (Taxon taxon : taxonsets) {
-            if (taxon.getID().equals(taxonSetID)) {
-                taxonset = taxon;
-                break;
-            }
-        }
-
-        if (taxonset.getID().equals(ALL_TAXA)) {
-            taxonset = null;
-        }
-        try {
-            // find TipDatesSampler and set TaxonSet input
-
-            String treeID = tree.getID();
-            String operatorID = "allTipDatesRandomWalker.t:" + treeID.substring(treeID.lastIndexOf(":") + 1);
-            TipDatesRandomWalker operator = (TipDatesRandomWalker) doc.pluginmap.get(operatorID);
-            Log.warning.println("treeID = " + treeID);
-            Log.warning.println("operatorID = " + operatorID);
-            Log.warning.println("operator = " + operator);
-            operator.m_taxonsetInput.setValue(taxonset, operator);
-
-//            for (BEASTObject beastObject : traitSet.outputs) {
-//                if (beastObject instanceof Tree) {
-//                    for (BEASTObject beastObject2 : beastObject.outputs) {
-//                        if (beastObject2 instanceof TipDatesScaler) {
-//                            TipDatesScaler operator = (TipDatesScaler) beastObject2;
-//                            operator.m_taxonsetInput.setValue(taxonset, operator);
-//                        }
-//                    }
-//                }
-//            }
-//
-//            // TODO: find MRACPriors and set TaxonSet inputs
-//            for (BEASTObject beastObject : traitSet.outputs) {
-//                if (beastObject instanceof Tree) {
-//                    for (BEASTObject beastObject2 : beastObject.outputs) {
-//                        if (beastObject2 instanceof MRCAPrior) {
-//                            MRCAPrior prior = (MRCAPrior) beastObject2;
-//                            if (prior.m_bOnlyUseTipsInput.get()) {
-//                                prior.m_taxonset.setValue(taxonset, prior);
-//                            }
-//                        }
-//                    }
-//                }
-//            }
-        } catch (Exception ex) {
-            // TODO: handle exception
-            ex.printStackTrace();
-        }
-    }
-
-    private void selectMode(ActionEvent e) {
-        JComboBox<?> comboBox = (JComboBox<?>) e.getSource();
-        m_iMode = comboBox.getSelectedIndex();
-        try {
-            // clear
-            for (Object beastObject : traitSet.getOutputs()) {
-                if (beastObject instanceof Tree) {
-                    for (Object beastObject2 : BEASTInterface.getOutputs(beastObject)) {
-                        if (beastObject2 instanceof TipDatesRandomWalker) {
-                            TipDatesRandomWalker operator = (TipDatesRandomWalker) beastObject2;
-                            switch (m_iMode) {
-                                case NO_TIP_SAMPLING:
-                                    operator.m_pWeight.setValue(0.0, operator);
-                                    break;
-                                case SAMPLE_TIPS_SAME_PRIOR:
-                                    if (operator.getID().contains("allTipDatesRandomWalker")) {
-                                        operator.m_pWeight.setValue(1.0, operator);
-                                    } else {
-                                        operator.m_pWeight.setValue(0.0, operator);
-                                    }
-                                    break;
-                                case SAMPLE_TIPS_MULTIPLE_PRIOR:
-                                    if (operator.getID().contains("allTipDatesRandomWalker")) {
-                                        operator.m_pWeight.setValue(0.0, operator);
-                                    } else {
-                                        operator.m_pWeight.setValue(0.1, operator);
-                                    }
-                                    break;
-                            }
-                        }
-                    }
-                }
-            }
-        } catch (Exception ex) {
-            // TODO: handle exception
-        }
-    }
 
     private Component createListBox() {
         taxa = traitSet.taxaInput.get().asStringList();
diff --git a/src/beast/app/draw/BEASTObjectInputEditor.java b/src/beast/app/draw/BEASTObjectInputEditor.java
index 4f7942f..31fbdbe 100644
--- a/src/beast/app/draw/BEASTObjectInputEditor.java
+++ b/src/beast/app/draw/BEASTObjectInputEditor.java
@@ -327,7 +327,8 @@ public class BEASTObjectInputEditor extends InputEditor.Base {
             });
 
             m_selectBEASTObjectBox.setToolTipText(input.getHTMLTipText());
-            m_selectBEASTObjectBox.setMaximumSize(new Dimension(1024, 200));
+            int fontsize = m_selectBEASTObjectBox.getFont().getSize();
+            m_selectBEASTObjectBox.setMaximumSize(new Dimension(1024, 200 * fontsize / 13));
             box.add(m_selectBEASTObjectBox);
         }
     }
diff --git a/src/beast/app/draw/DoubleListInputEditor.java b/src/beast/app/draw/DoubleListInputEditor.java
index 9fe6495..785d921 100644
--- a/src/beast/app/draw/DoubleListInputEditor.java
+++ b/src/beast/app/draw/DoubleListInputEditor.java
@@ -169,12 +169,14 @@ public class DoubleListInputEditor extends ListInputEditor {
 		void setUpEntry() {
             m_entry = new JTextField();
             m_entry.setName(m_input.getName());
+            int size = m_entry.getFont().getSize();
+            PREFERRED_SIZE = new Dimension(200, 25 * size / 13);
             m_entry.setMinimumSize(PREFERRED_SIZE);
             m_entry.setPreferredSize(PREFERRED_SIZE);
             m_entry.setSize(PREFERRED_SIZE);
             initEntry();
             m_entry.setToolTipText(m_input.getHTMLTipText());
-            m_entry.setMaximumSize(MAX_SIZE);
+            m_entry.setMaximumSize(new Dimension(1024, 25 * size / 13));
 
             m_entry.getDocument().addDocumentListener(new DocumentListener() {
                 @Override
@@ -260,7 +262,8 @@ public class DoubleListInputEditor extends ListInputEditor {
                 m_inputLabel.setToolTipText(tipText);
                 m_inputLabel.setHorizontalTextPosition(SwingConstants.RIGHT);
                 //Dimension size = new Dimension(g_nLabelWidth, 20);
-                Dimension size = new Dimension(200, 20);
+                int fontsize = m_inputLabel.getFont().getSize();
+                Dimension size = new Dimension(200, 20 * fontsize / 13);
                 m_inputLabel.setMaximumSize(size);
                 m_inputLabel.setMinimumSize(size);
                 m_inputLabel.setPreferredSize(size);
diff --git a/src/beast/app/draw/EnumInputEditor.java b/src/beast/app/draw/EnumInputEditor.java
index 73869d3..965ddf3 100644
--- a/src/beast/app/draw/EnumInputEditor.java
+++ b/src/beast/app/draw/EnumInputEditor.java
@@ -1,5 +1,6 @@
 package beast.app.draw;
 
+import java.awt.Dimension;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -48,6 +49,9 @@ public class EnumInputEditor extends InputEditor.Base {
         }
         if (availableValues.size() > 1) {
             m_selectPluginBox = new JComboBox<>(availableValues.toArray(new String[0]));
+            Dimension maxDim = m_selectPluginBox.getPreferredSize();
+            m_selectPluginBox.setMaximumSize(maxDim);
+
             String selectString = input.get().toString();
             m_selectPluginBox.setSelectedItem(selectString);
 
diff --git a/src/beast/app/draw/InputEditor.java b/src/beast/app/draw/InputEditor.java
index 7c6126a..01d0763 100644
--- a/src/beast/app/draw/InputEditor.java
+++ b/src/beast/app/draw/InputEditor.java
@@ -315,7 +315,7 @@ public abstract class Base extends JPanel implements InputEditor {
             m_inputLabel.setHorizontalTextPosition(SwingConstants.RIGHT);
             //Dimension size = new Dimension(g_nLabelWidth, 20);
             int fontsize = m_inputLabel.getFont().getSize();
-            Dimension size = new Dimension(200, 20 * fontsize / 13);
+            Dimension size = new Dimension(200 * fontsize / 13, 20 * fontsize / 13);
             m_inputLabel.setMaximumSize(size);
             m_inputLabel.setMinimumSize(size);
             m_inputLabel.setPreferredSize(size);
diff --git a/src/beast/app/draw/IntegerListInputEditor.java b/src/beast/app/draw/IntegerListInputEditor.java
index 7eef756..599eb6c 100644
--- a/src/beast/app/draw/IntegerListInputEditor.java
+++ b/src/beast/app/draw/IntegerListInputEditor.java
@@ -278,7 +278,8 @@ public class IntegerListInputEditor extends ListInputEditor {
                 m_inputLabel.setToolTipText(tipText);
                 m_inputLabel.setHorizontalTextPosition(SwingConstants.RIGHT);
                 //Dimension size = new Dimension(g_nLabelWidth, 20);
-                Dimension size = new Dimension(200, 20);
+                int fontsize = m_inputLabel.getFont().getSize();
+                Dimension size = new Dimension(200 * fontsize / 13, 20 * fontsize / 13);
                 m_inputLabel.setMaximumSize(size);
                 m_inputLabel.setMinimumSize(size);
                 m_inputLabel.setPreferredSize(size);
diff --git a/src/beast/app/tools/LogCombiner.java b/src/beast/app/tools/LogCombiner.java
index decf417..7e10945 100644
--- a/src/beast/app/tools/LogCombiner.java
+++ b/src/beast/app/tools/LogCombiner.java
@@ -325,8 +325,8 @@ public class LogCombiner extends LogAnalyser {
 	                	} else {
 	                		state += m_nSampleInterval;
 	                	}
-	                    str = str.replaceAll("^tree STATE_[^\\s=]*", "");
-	                	m_out.print("tree STATE_" + state + " =" + str);
+	                    str = str.replaceAll("^tree STATE_[^\\s]*", "");
+	                	m_out.print("tree STATE_" + state + str);
 	                	m_out.println();
                 	}
                 }
@@ -539,8 +539,10 @@ public class LogCombiner extends LogAnalyser {
                 String titleString = "<html><center><p>LogCombiner<br>" +
                         "Version " + version.getVersionString() + ", " + version.getDateString() + "</p></center></html>";
 
-                //ConsoleApplication consoleApp =
                 new ConsoleApplication(nameString, aboutString, icon, true);
+                Log.info = System.out;
+                Log.warning = System.out;
+                Log.err = System.err;
 
                 combiner.printTitle(aboutString);
 
diff --git a/src/beast/app/treeannotator/TreeAnnotator.java b/src/beast/app/treeannotator/TreeAnnotator.java
index b16aca7..5601b64 100644
--- a/src/beast/app/treeannotator/TreeAnnotator.java
+++ b/src/beast/app/treeannotator/TreeAnnotator.java
@@ -1291,6 +1291,8 @@ public class TreeAnnotator {
                     "</center></html>";
 
             new ConsoleApplication(nameString, aboutString, icon, true);
+            Log.info = System.out;
+            Log.err = System.err;
 
             // The ConsoleApplication will have overridden System.out so set progressStream
             // to capture the output to the window:
diff --git a/src/beast/app/util/Utils.java b/src/beast/app/util/Utils.java
index c54b13f..65a85ab 100644
--- a/src/beast/app/util/Utils.java
+++ b/src/beast/app/util/Utils.java
@@ -278,7 +278,7 @@ public class Utils {
             chooser.setMultiSelectionEnabled(allowMultipleSelection);
             //chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
 
-            if (description != null) {
+            if (description != null && extensions.length > 1 && extensions[0].length() > 0) {
                 FileNameExtensionFilter filter = new FileNameExtensionFilter(description, extensions);
                 chooser.setFileFilter(filter);
             }
diff --git a/src/beast/core/BEASTInterface.java b/src/beast/core/BEASTInterface.java
index fe45033..0ff64e5 100644
--- a/src/beast/core/BEASTInterface.java
+++ b/src/beast/core/BEASTInterface.java
@@ -131,19 +131,44 @@ public interface BEASTInterface {
     }
 
     /**
-     * @return citation from @Citation annotation *
+     * Deprecated: use getCitationList() instead to allow multiple citations, not just the first one
      */
+	@Deprecated
     default public Citation getCitation() {
         final Annotation[] classAnnotations = this.getClass().getAnnotations();
         for (final Annotation annotation : classAnnotations) {
             if (annotation instanceof Citation) {
                 return (Citation) annotation;
             }
+            if (annotation instanceof Citation.Citations) {
+                return ((Citation.Citations) annotation).value()[0];
+                // TODO: this ignores other citations
+            }
         }
         return null;
     }
 
     /**
+     * @return array of @Citation annotations for this class
+     * or empty list if there are no citations
+     **/
+    default public List<Citation> getCitationList() {
+        final Annotation[] classAnnotations = this.getClass().getAnnotations();
+        List<Citation> citations = new ArrayList<>();
+        for (final Annotation annotation : classAnnotations) {
+            if (annotation instanceof Citation) {
+            	citations.add((Citation) annotation);
+            }
+            if (annotation instanceof Citation.Citations) {
+            	for (Citation citation : ((Citation.Citations) annotation).value()) {
+            		citations.add(citation);
+            	}
+            }
+        }
+       	return citations;
+    }
+
+    /**
      * @return references for this plug in and all its inputs *
      */
     default public String getCitations() {
@@ -158,16 +183,15 @@ public interface BEASTInterface {
             IDs.add(getID());
         }
         final StringBuilder buf = new StringBuilder();
-        if (getCitation() != null) {
-            // only add citation if it is not already processed
-            if (!citations.contains(getCitation().value())) {
+        // only add citation if it is not already processed
+    	for (Citation citation : getCitationList()) {
+            if (!citations.contains(citation.value())) {
                 // and there is actually a citation to add
                 buf.append("\n");
-                buf.append(getCitation().value());
+                buf.append(citation.value());
                 buf.append("\n");
-                citations.add(getCitation().value());
+                citations.add(citation.value());
             }
-            //return buf.toString();
         }
         try {
             for (final BEASTInterface beastObject : listActiveBEASTObjects()) {
diff --git a/src/beast/core/Citation.java b/src/beast/core/Citation.java
index 45c9739..b2457bf 100644
--- a/src/beast/core/Citation.java
+++ b/src/beast/core/Citation.java
@@ -27,6 +27,7 @@ package beast.core;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
@@ -42,9 +43,10 @@ import java.lang.annotation.Target;
  * like DocMaker then can pick it up through introspection.
  * <p/>
  */
- at Target({ElementType.TYPE})
+//@Target({ElementType.TYPE}) //<- does not work together with @Repeatable
 @Retention(RetentionPolicy.RUNTIME)
 @Inherited
+ at Repeatable(Citation.Citations.class)
 public @interface Citation {
 
     /**
@@ -57,4 +59,16 @@ public @interface Citation {
     int year() default 0;
     
     String firstAuthorSurname() default "";
-}
\ No newline at end of file
+
+    /** 
+     * The Citations annotation is required to retrieve classes annotated with 
+     * multiple citations.
+     **/
+    @Inherited
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Citations {
+    	Citation[] value();
+    }
+}
+
+
diff --git a/src/beast/core/Logger.java b/src/beast/core/Logger.java
index a00fbf8..1ba1f9b 100644
--- a/src/beast/core/Logger.java
+++ b/src/beast/core/Logger.java
@@ -266,7 +266,7 @@ public class Logger extends BEASTObject {
     	for (int i = 0; i < header.length(); i++) {
     		char c = header.charAt(i);
     		if (c == '.') {
-    			if (header.charAt(i+2) == ':') {
+    			if (i < header.length() - 2 && header.charAt(i+2) == ':') {
     				final char c2 = header.charAt(++i);
     				i++;
     				String prefix = "";
diff --git a/src/beast/core/parameter/Parameter.java b/src/beast/core/parameter/Parameter.java
index 8f69ee1..00269f7 100644
--- a/src/beast/core/parameter/Parameter.java
+++ b/src/beast/core/parameter/Parameter.java
@@ -332,7 +332,7 @@ public interface Parameter<T> extends Function {
         public void assignFromFragile(final StateNode other) {
             @SuppressWarnings("unchecked")
             final Parameter.Base<T> source = (Parameter.Base<T>) other;
-            System.arraycopy(source.values, 0, values, 0, values.length);
+            System.arraycopy(source.values, 0, values, 0, Math.min(values.length, source.getDimension()));
             Arrays.fill(m_bIsDirty, false);
         }
 
diff --git a/src/beast/core/util/Sum.java b/src/beast/core/util/Sum.java
index 5213225..0b37a99 100644
--- a/src/beast/core/util/Sum.java
+++ b/src/beast/core/util/Sum.java
@@ -1,6 +1,8 @@
 package beast.core.util;
 
 import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
 
 import beast.core.BEASTObject;
 import beast.core.CalculationNode;
@@ -16,7 +18,7 @@ import beast.core.parameter.IntegerParameter;
 
 @Description("calculates sum of a valuable")
 public class Sum extends CalculationNode implements Function, Loggable {
-    final public Input<Function> functionInput = new Input<>("arg", "argument to be summed", Validate.REQUIRED);
+    final public Input<List<Function>> functionInput = new Input<>("arg", "argument to be summed", new ArrayList<>(), Validate.REQUIRED);
 
     enum Mode {integer_mode, double_mode}
 
@@ -28,11 +30,12 @@ public class Sum extends CalculationNode implements Function, Loggable {
 
     @Override
     public void initAndValidate() {
-        Function valuable = functionInput.get();
-        if (valuable instanceof IntegerParameter || valuable instanceof BooleanParameter) {
-            mode = Mode.integer_mode;
-        } else {
-            mode = Mode.double_mode;
+        List<Function> valuable = functionInput.get();
+        mode = Mode.integer_mode;
+        for (Function v : valuable) {
+	        if (!(v instanceof IntegerParameter || v instanceof BooleanParameter)) {
+	            mode = Mode.double_mode;
+	        }
         }
     }
 
@@ -54,9 +57,10 @@ public class Sum extends CalculationNode implements Function, Loggable {
      */
     void compute() {
         sum = 0;
-        final Function v = functionInput.get();
-        for (int i = 0; i < v.getDimension(); i++) {
-            sum += v.getArrayValue(i);
+        for (Function v : functionInput.get()) {
+	        for (int i = 0; i < v.getDimension(); i++) {
+	            sum += v.getArrayValue(i);
+	        }
         }
         needsRecompute = false;
     }
@@ -95,16 +99,16 @@ public class Sum extends CalculationNode implements Function, Loggable {
      */
     @Override
     public void init(PrintStream out) {
-        out.print("sum(" + ((BEASTObject) functionInput.get()).getID() + ")\t");
+        out.print("sum(" + ((BEASTObject) functionInput.get().get(0)).getID() + ")\t");
     }
 
     @Override
     public void log(int sampleNr, PrintStream out) {
-        Function valuable = functionInput.get();
-        final int dimension = valuable.getDimension();
         double sum = 0;
-        for (int i = 0; i < dimension; i++) {
-            sum += valuable.getArrayValue(i);
+        for (Function v : functionInput.get()) {
+	        for (int i = 0; i < v.getDimension(); i++) {
+	            sum += v.getArrayValue(i);
+	        }
         }
         if (mode == Mode.integer_mode) {
             out.print((int) sum + "\t");
diff --git a/src/beast/evolution/alignment/TaxonSet.java b/src/beast/evolution/alignment/TaxonSet.java
index 79034b2..4183cf5 100644
--- a/src/beast/evolution/alignment/TaxonSet.java
+++ b/src/beast/evolution/alignment/TaxonSet.java
@@ -50,12 +50,12 @@ public class TaxonSet extends Taxon {
         taxonList = taxonsetInput.get();
         if (alignmentInput.get() != null) {
             if (taxonList.size() > 0) {
-                throw new IllegalArgumentException("Only one of taxon and alignment should be specified, not both.");
+                throw new IllegalArgumentException("Only one of taxon and alignment should be specified, not both (id=" + getID() + ").");
             }
             taxaNames = alignmentInput.get().taxaNames;
         } else {
             if (taxonList.size() == 0) {
-                throw new IllegalArgumentException("Either taxon or alignment should be specified.");
+                throw new IllegalArgumentException(getID() + ": Either taxon or alignment should be specified (id=" + getID() + ").");
             }
             taxaNames = new ArrayList<>();
             for (final Taxon taxon : taxonList) {
diff --git a/src/beast/evolution/datatype/IntegerData.java b/src/beast/evolution/datatype/IntegerData.java
index 136d82d..77d7115 100644
--- a/src/beast/evolution/datatype/IntegerData.java
+++ b/src/beast/evolution/datatype/IntegerData.java
@@ -31,4 +31,11 @@ public class IntegerData extends Base {
         return (char)('0'+state);
     }
 
+    @Override
+    public String getCode(int state) {
+    	if (state < 0) {
+    		return "?";
+    	}
+    	return state + "";
+    }
 }
diff --git a/src/beast/evolution/likelihood/ThreadedTreeLikelihood.java b/src/beast/evolution/likelihood/ThreadedTreeLikelihood.java
index f485262..f23af2a 100644
--- a/src/beast/evolution/likelihood/ThreadedTreeLikelihood.java
+++ b/src/beast/evolution/likelihood/ThreadedTreeLikelihood.java
@@ -58,12 +58,12 @@ import beast.evolution.substitutionmodel.SubstitutionModel;
 		"a variant of the 'peeling algorithm'. For details, see" +
 		"Felsenstein, Joseph (1981). Evolutionary trees from DNA sequences: a maximum likelihood approach. J Mol Evol 17 (6): 368-376.")
 public class ThreadedTreeLikelihood extends GenericTreeLikelihood {
-    final public Input<Boolean> useAmbiguitiesInput = new Input<>("useAmbiguities", "flag to indicate leafs that sites containing ambigue states should be handled instead of ignored (the default)", false);
+    final public Input<Boolean> useAmbiguitiesInput = new Input<>("useAmbiguities", "flag to indicate leafs that sites containing ambiguous states should be handled instead of ignored (the default)", false);
     
     final public Input<Integer> maxNrOfThreadsInput = new Input<>("threads","maximum number of threads to use, if less than 1 the number of threads in BeastMCMC is used (default -1)", -1);
 
     final public Input<String> proportionsInput = new Input<>("proportions", "specifies proportions of patterns used per thread as space "
-    		+ "delimted string. This is useful when using a mixture of BEAGLE devices that run at different speeds, e.g GPU and CPU. "
+    		+ "delimited string. This is useful when using a mixture of BEAGLE devices that run at different speeds, e.g GPU and CPU. "
     		+ "The string is duplicated if there are more threads than proportions specified. For example, "
     		+ "'1 2' as well as '33 66' with 2 threads specifies that the first thread gets a third of the patterns and the second "
     		+ "two thirds. With 3 threads, it is interpreted as '1 2 1' = 25%, 50%, 25% and with 7 threads it is "
@@ -123,7 +123,7 @@ public class ThreadedTreeLikelihood extends GenericTreeLikelihood {
     	treelikelihood = new TreeLikelihood[threadCount];
     	
     	if (dataInput.get().isAscertained) {
-    		Log.warning.println("Note, because the alignment is ascertained -- can only use single trhead per alignment");
+    		Log.warning.println("Note, can only use single thread per alignment because the alignment is ascertained");
     		threadCount = 1;
     	}
     	
@@ -170,7 +170,7 @@ public class ThreadedTreeLikelihood extends GenericTreeLikelihood {
         				"siteModel", duplicate((BEASTInterface) siteModelInput.get(), i), 
         				"branchRateModel", duplicate(branchRateModelInput.get(), i), 
         				"useAmbiguities", useAmbiguitiesInput.get(),
-						"scaling" , scalingInput.get() + ""
+                                    	"scaling" , scalingInput.get() + ""
         				);
         		
         		likelihoodCallers.add(new TreeLikelihoodCaller(treelikelihood[i], i));
diff --git a/src/beast/evolution/speciation/CalibratedYuleModel.java b/src/beast/evolution/speciation/CalibratedYuleModel.java
index 7a7ae35..511c8b6 100644
--- a/src/beast/evolution/speciation/CalibratedYuleModel.java
+++ b/src/beast/evolution/speciation/CalibratedYuleModel.java
@@ -132,6 +132,7 @@ public class CalibratedYuleModel extends SpeciesTreeDistribution {
                                 final CalibrationPoint cal = new CalibrationPoint();
                                 cal.distInput.setValue(_MRCAPrior.distInput.get(), cal);
                                 cal.taxonsetInput.setValue(_MRCAPrior.taxonsetInput.get(), cal);
+                                cal.forParentInput.setValue(_MRCAPrior.useOriginateInput.get(), cal);
                                 cal.initAndValidate();
                                 cals.add(cal);
                                 taxaSets.add(cal.taxa());
diff --git a/src/beast/evolution/tree/RandomTree.java b/src/beast/evolution/tree/RandomTree.java
index 61df99e..fdd9df1 100644
--- a/src/beast/evolution/tree/RandomTree.java
+++ b/src/beast/evolution/tree/RandomTree.java
@@ -203,7 +203,7 @@ public class RandomTree extends Tree implements StateNodeInitialiser {
         for (final MRCAPrior prior : calibrations) {
             final TaxonSet taxonSet = prior.taxonsetInput.get();
             if (taxonSet != null && !prior.onlyUseTipsInput.get()) {
-	            final Set<String> usedTaxa = new HashSet<>();
+	            final Set<String> usedTaxa = new LinkedHashSet<>();
 	        	if (taxonSet.asStringList() == null) {
 	        		taxonSet.initAndValidate();
 	        	}
@@ -257,7 +257,7 @@ public class RandomTree extends Tree implements StateNodeInitialiser {
         for (int i = 0; i < lastMonophyletic; i++) {
             for (int j = i + 1; j < lastMonophyletic; j++) {
 
-                Set<String> intersection = new HashSet<>(taxonSets.get(i));
+                Set<String> intersection = new LinkedHashSet<>(taxonSets.get(i));
                 intersection.retainAll(taxonSets.get(j));
 
                 if (intersection.size() > 0) {
@@ -351,7 +351,7 @@ public class RandomTree extends Tree implements StateNodeInitialiser {
                 if (taxonSet == null) {
                 	throw new IllegalArgumentException("Something is wrong with constraint " + p.getID() + " -- a taxonset must be specified if a monophyletic constraint is enforced.");
                 }
-                final Set<String> usedTaxa = new HashSet<>();
+                final Set<String> usedTaxa = new LinkedHashSet<>();
                 usedTaxa.addAll(taxonSet.asStringList());
                 /* int c = */ traverse(root, usedTaxa, taxonSet.getTaxonCount(), new int[1]);
                 // boolean b = c == nrOfTaxa + 127;
@@ -418,7 +418,7 @@ public class RandomTree extends Tree implements StateNodeInitialiser {
         for (int attempts = 0; attempts < 1000; ++attempts) {
             try {
                 nextNodeNr = nrOfTaxa;
-                final Set<Node> candidates = new HashSet<>();
+                final Set<Node> candidates = new LinkedHashSet<>();
                 int i = 0;
                 for (String taxon : taxa) {
                     final Node node = newNode();
@@ -450,8 +450,9 @@ public class RandomTree extends Tree implements StateNodeInitialiser {
             			+ "be nested clades.\n";
             	msg += "2. clade heights are constrained by an upper and lower bound, but the population size \n"
             			+ "is too large, so it is very unlikely a generated treed does not violate these constraints. To \n"
-            			+ "fix this you can try to reduce the popultion size of the population model.\n";
+            			+ "fix this you can try to reduce the population size of the population model.\n";
             	msg += "Expect BEAST to crash if this is not fixed.\n"; 
+            	Log.err.println(msg);
             }
         }
         throw new RuntimeException(msg);
@@ -477,7 +478,7 @@ public class RandomTree extends Tree implements StateNodeInitialiser {
         final Set<String> taxaDone = new TreeSet<>();
         for (final int monoNode : children[isMonophyleticNode]) {
             // create list of leaf nodes for this monophyletic MRCA
-            final Set<Node> candidates2 = new HashSet<>();
+            final Set<Node> candidates2 = new LinkedHashSet<>();
             final Set<String> isTaxonSet = taxonSets.get(monoNode);
             for (String taxon : isTaxonSet) {
                 candidates2.add(allCandidates.get(taxon));
@@ -743,7 +744,8 @@ public class RandomTree extends Tree implements StateNodeInitialiser {
 
         if (getMinimumInactiveHeight() < height) {
             throw new RuntimeException(
-                    "This should never happen! Somehow the current active node is older than the next inactive node!");
+                    "This should never happen! Somehow the current active node is older than the next inactive node!\n"
+            		+ "One possible solution you can try is to increase the population size of the population model.");
         }
         return height;
     }
diff --git a/src/beast/evolution/tree/TraitSet.java b/src/beast/evolution/tree/TraitSet.java
index e95cc84..afa94bd 100644
--- a/src/beast/evolution/tree/TraitSet.java
+++ b/src/beast/evolution/tree/TraitSet.java
@@ -98,7 +98,8 @@ public class TraitSet extends BEASTObject {
         // sanity check: did we cover all taxa?
         for (int i = 0; i < labels.size(); i++) {
             if (taxonValues[i] == null) {
-                Log.warning.println("WARNING: no trait specified for " + labels.get(i));
+                Log.warning.println("WARNING: no trait specified for " + labels.get(i) +": Assumed to be 0");
+                map.put(labels.get(i), i);
             }
         }
 
diff --git a/src/beast/evolution/tree/Tree.java b/src/beast/evolution/tree/Tree.java
index ee00c05..7bd4715 100644
--- a/src/beast/evolution/tree/Tree.java
+++ b/src/beast/evolution/tree/Tree.java
@@ -189,6 +189,7 @@ public class Tree extends StateNode implements TreeInterface {
         m_storedNodes = new Node[nodeCount];
         final Node copy = root.copy();
         listNodes(copy, m_storedNodes);
+        postCache = null;
     }
 
 
diff --git a/src/beast/evolution/tree/TreeHeightLogger.java b/src/beast/evolution/tree/TreeHeightLogger.java
index 2c93418..a1364ec 100644
--- a/src/beast/evolution/tree/TreeHeightLogger.java
+++ b/src/beast/evolution/tree/TreeHeightLogger.java
@@ -12,10 +12,11 @@ import beast.core.Input.Validate;
 import beast.core.Loggable;
 
 
- at Description("Logger to report height of a tree")
+ at Description("Logger to report height of a tree -- deprecated: use TreeStatLogger instead")
+ at Deprecated
 public class TreeHeightLogger extends CalculationNode implements Loggable, Function {
     final public Input<Tree> treeInput = new Input<>("tree", "tree to report height for.", Validate.REQUIRED);
-
+    
     @Override
     public void initAndValidate() {
         // nothing to do
@@ -37,7 +38,7 @@ public class TreeHeightLogger extends CalculationNode implements Loggable, Funct
         out.print(tree.getRoot().getHeight() + "\t");
     }
 
-    @Override
+	@Override
     public void close(PrintStream out) {
         // nothing to do
     }
diff --git a/src/beast/evolution/tree/TreeStatLogger.java b/src/beast/evolution/tree/TreeStatLogger.java
new file mode 100644
index 0000000..7c6cb88
--- /dev/null
+++ b/src/beast/evolution/tree/TreeStatLogger.java
@@ -0,0 +1,80 @@
+package beast.evolution.tree;
+
+
+
+import java.io.PrintStream;
+
+import beast.core.CalculationNode;
+import beast.core.Description;
+import beast.core.Function;
+import beast.core.Input;
+import beast.core.Input.Validate;
+import beast.core.util.Log;
+import beast.core.Loggable;
+
+
+ at Description("Logger to report statistics of a tree")
+public class TreeStatLogger extends CalculationNode implements Loggable, Function {
+    final public Input<Tree> treeInput = new Input<>("tree", "tree to report height for.", Validate.REQUIRED);
+    final public Input<Boolean> logHeigthInput = new Input<>("logHeigth", "If true, tree height will be logged.", true);
+    final public Input<Boolean> logLengthInput = new Input<>("logLength", "If true, tree length will be logged.", true);
+
+    @Override
+    public void initAndValidate() {
+    	if (!logHeigthInput.get() && !logLengthInput.get()) {
+    		Log.warning.println("TreeStatLogger " + getID() + "logs nothing. Set logHeigth=true or logLength=true to log at least something");
+    	}
+    }
+
+    @Override
+    public void init(PrintStream out) {
+        final Tree tree = treeInput.get();
+        if (logHeigthInput.get()) {
+            out.print(tree.getID() + ".height\t");
+        }
+        if (logLengthInput.get()) {
+            out.print(tree.getID() + ".treeLength\t");
+        }
+    }
+
+    @Override
+    public void log(int sample, PrintStream out) {
+        final Tree tree = treeInput.get();
+        if (logHeigthInput.get()) {
+        	out.print(tree.getRoot().getHeight() + "\t");
+        }
+        if (logLengthInput.get()) {
+            out.print(getLength(tree) + "\t");
+        }
+    }
+
+    private double getLength(Tree tree) {
+    	double length = 0;
+    	for (Node node : tree.getNodesAsArray()) {
+    		if (!node.isRoot()) {
+    			length += node.getLength();
+    		}
+    	}
+		return length;
+	}
+
+	@Override
+    public void close(PrintStream out) {
+        // nothing to do
+    }
+
+    @Override
+    public int getDimension() {
+        return 1;
+    }
+
+    @Override
+    public double getArrayValue() {
+        return treeInput.get().getRoot().getHeight();
+    }
+
+    @Override
+    public double getArrayValue(int dim) {
+        return treeInput.get().getRoot().getHeight();
+    }
+}
diff --git a/src/beast/evolution/tree/coalescent/ExponentialGrowth.java b/src/beast/evolution/tree/coalescent/ExponentialGrowth.java
index 879e782..d10d88b 100644
--- a/src/beast/evolution/tree/coalescent/ExponentialGrowth.java
+++ b/src/beast/evolution/tree/coalescent/ExponentialGrowth.java
@@ -43,7 +43,10 @@ public class ExponentialGrowth extends PopulationFunction.Abstract {
     final public Input<RealParameter> popSizeParameterInput = new Input<>("popSize",
             "present-day population size (defaults to 1.0). ");
     final public Input<RealParameter> growthRateParameterInput = new Input<>("growthRate",
-            "growth rate is the exponent of the exponential growth");
+            "Growth rate is the exponent of the exponential growth. " +
+            "A value of zero represents a constant population size, negative values represent " +
+            "decline towards the present, positive numbers represents exponential growth towards " +
+            "the present.");
 
     //
     // Public stuff
diff --git a/src/beast/evolution/tree/coalescent/TreeIntervals.java b/src/beast/evolution/tree/coalescent/TreeIntervals.java
index c79c9c8..9aa7ae4 100644
--- a/src/beast/evolution/tree/coalescent/TreeIntervals.java
+++ b/src/beast/evolution/tree/coalescent/TreeIntervals.java
@@ -324,12 +324,12 @@ public class TreeIntervals extends CalculationNode implements IntervalList {
 
         final int nodeCount = tree.getNodeCount();
 
-        double[] times = new double[nodeCount];
+        times = new double[nodeCount];
         int[] childCounts = new int[nodeCount];
 
         collectTimes(tree, times, childCounts);
 
-        int[] indices = new int[nodeCount];
+        indices = new int[nodeCount];
 
         HeapSort.sort(times, indices);
 
@@ -430,6 +430,19 @@ public class TreeIntervals extends CalculationNode implements IntervalList {
         intervalsKnown = true;
     }
 
+    /**
+     * Returns the time of the start of an interval
+     *
+     * @param i which interval
+     * @return start time
+     */
+    public double getIntervalTime(int i) {
+        if (!intervalsKnown) {
+            calculateIntervals();
+        }
+        return times[indices[i]];
+    }
+    
     protected void addLineage(int interval, Node node) {
         if (lineagesAdded[interval] == null) lineagesAdded[interval] = new ArrayList<>();
         lineagesAdded[interval].add(node);
@@ -475,6 +488,10 @@ public class TreeIntervals extends CalculationNode implements IntervalList {
     protected double[] intervals;
     protected double[] storedIntervals;
 
+    /** interval times **/
+    double[] times;
+    int[] indices;
+    
     /**
      * The number of uncoalesced lineages within a particular interval.
      */
diff --git a/src/beast/math/distributions/Gamma.java b/src/beast/math/distributions/Gamma.java
index 8e06ffc..43ea49f 100644
--- a/src/beast/math/distributions/Gamma.java
+++ b/src/beast/math/distributions/Gamma.java
@@ -13,12 +13,25 @@ import beast.core.parameter.RealParameter;
         "separate independent component.")
 public class Gamma extends ParametricDistribution {
     final public Input<RealParameter> alphaInput = new Input<>("alpha", "shape parameter, defaults to 2");
-    final public Input<RealParameter> betaInput = new Input<>("beta", "scale parameter, defaults to 2");
+    final public Input<RealParameter> betaInput = new Input<>("beta", "second parameter depends on mode, defaults to 2."
+    		+ "For mode=ShapeScale beta is interpreted as scale. "
+    		+ "For mode=ShapeRate beta is interpreted as rate. "
+    		+ "For mode=ShapeMean beta is interpreted as mean."
+    		+ "For mode=OneParameter beta is ignored.");
+    public enum mode {ShapeScale, ShapeRate, ShapeMean, OneParameter};
+    final public Input<mode> modeInput = new Input<>("mode", "determines parameterisation. "
+    		+ "For ShapeScale beta is interpreted as scale. "
+    		+ "For ShapeRate beta is interpreted as rate. "
+    		+ "For ShapeMean beta is interpreted as mean."
+    		+ "For OneParameter beta is ignored.", mode.ShapeScale, mode.values());
 
     static org.apache.commons.math.distribution.GammaDistribution m_dist = new GammaDistributionImpl(1, 1);
 
+    mode parameterisation = mode.ShapeScale;
+    		
     @Override
     public void initAndValidate() {
+    	parameterisation = modeInput.get();
         refresh();
     }
 
@@ -28,16 +41,32 @@ public class Gamma extends ParametricDistribution {
     @SuppressWarnings("deprecation")
 	void refresh() {
         double alpha;
-        double beta;
+        double beta = 2.0;
         if (alphaInput.get() == null) {
             alpha = 2;
         } else {
             alpha = alphaInput.get().getValue();
         }
-        if (betaInput.get() == null) {
-            beta = 2;
-        } else {
-            beta = betaInput.get().getValue();
+        
+        switch (parameterisation) {
+        case ShapeScale:
+            if (betaInput.get() != null) {
+                beta = betaInput.get().getValue();
+            }
+        	break;
+        case ShapeRate:
+            if (betaInput.get() != null) {
+                beta = 1.0/betaInput.get().getValue();
+            }
+        	break;
+        case ShapeMean:
+            if (betaInput.get() != null) {
+                beta = betaInput.get().getValue() / alpha;
+            }
+        	break;
+        case OneParameter:
+        	beta = 1.0 / alpha;
+        	break;
         }
         m_dist.setAlpha(alpha);
         m_dist.setBeta(beta);
@@ -51,6 +80,7 @@ public class Gamma extends ParametricDistribution {
 
     @Override
     public double getMean() {
+    	refresh();
     	return offsetInput.get() + m_dist.getAlpha() * m_dist.getBeta();
     }
 } // class Gamma
diff --git a/src/beast/math/distributions/MRCAPrior.java b/src/beast/math/distributions/MRCAPrior.java
index 238c584..b564981 100644
--- a/src/beast/math/distributions/MRCAPrior.java
+++ b/src/beast/math/distributions/MRCAPrior.java
@@ -366,7 +366,7 @@ public class MRCAPrior extends Distribution {
             if (dist != null) {
                 out.print("logP(mrca(" + taxonsetInput.get().getID() + "))\t");
             }
-            out.print("mrcatime(" + taxonsetInput.get().getID() + ")\t");
+            out.print("mrcatime(" + taxonsetInput.get().getID() + (useOriginate ? ".originate" : "") +")\t");
         }
     }
 
diff --git a/src/beast/util/AddOnManager.java b/src/beast/util/AddOnManager.java
index 37e5ac8..05f55a9 100644
--- a/src/beast/util/AddOnManager.java
+++ b/src/beast/util/AddOnManager.java
@@ -62,6 +62,7 @@ import beast.app.BEASTVersion2;
 import beast.app.beastapp.BeastMain;
 import beast.app.util.Arguments;
 import beast.app.util.Utils;
+import beast.core.BEASTInterface;
 import beast.core.Description;
 import beast.core.util.Log;
 import beast.evolution.alignment.Alignment;
@@ -218,7 +219,15 @@ public class AddOnManager {
                     packageMap.put(packageName, pkg);
                 }
 
+                if (addon.hasAttribute("projectURL"))
+                    pkg.setProjectURL(new URL(addon.getAttribute("projectURL")));
+
                 PackageVersion installedVersion = new PackageVersion(packageVersionString);
+
+                if (addon.hasAttribute("projectURL") &&
+                        !(pkg.getLatestVersion() != null && installedVersion.compareTo(pkg.getLatestVersion())<0))
+                    pkg.setProjectURL(new URL(addon.getAttribute("projectURL")));
+
                 Set<PackageDependency> installedVersionDependencies =
                         new TreeSet<>((o1, o2) -> o1.dependencyName.compareTo(o2.dependencyName));
 
@@ -307,8 +316,12 @@ public class AddOnManager {
                         pkg.setDescription(element.getAttribute("description"));
 
                         PackageVersion packageVersion = new PackageVersion(element.getAttribute("version"));
-                        Set<PackageDependency> packageDependencies = new HashSet<>();
 
+                        if (element.hasAttribute("projectURL") &&
+                                !(pkg.getLatestVersion() != null && packageVersion.compareTo(pkg.getLatestVersion())<0))
+                            pkg.setProjectURL(new URL(element.getAttribute("projectURL")));
+
+                        Set<PackageDependency> packageDependencies = new HashSet<>();
                         NodeList depNodes = element.getElementsByTagName("depends");
                         for (int j = 0; j < depNodes.getLength(); j++) {
                             Element dependson = (Element) depNodes.item(j);
@@ -818,13 +831,14 @@ public class AddOnManager {
         return dirs;
     } // getBeastDirectories
 
-    /**
+    
+
+	/**
      * load external jars in beast directories *
      */
     public static void loadExternalJars() throws IOException {
         processDeleteList();
 
-        TreeMap<String, Package> packages = new TreeMap<>();
         addInstalledPackages(packages);
 
         processInstallList(packages);
@@ -833,13 +847,15 @@ public class AddOnManager {
 
         for (String jarDirName : getBeastDirectories()) {
             File versionFile = new File(jarDirName + "/version.xml");
+            String packageNameAndVersion = null;
             if (versionFile.exists()) {
                 try {
                     // print name and version of package
                     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                     Document doc = factory.newDocumentBuilder().parse(versionFile);
                     Element addon = doc.getDocumentElement();
-                    Log.warning.println("Loading package " + addon.getAttribute("name") + " v" + addon.getAttribute("version"));
+                    packageNameAndVersion = addon.getAttribute("name") + " v" + addon.getAttribute("version");
+                    Log.warning.println("Loading package " + packageNameAndVersion);
                 } catch (Exception e) {
                 	// too bad, won't print out any info
                 }
@@ -873,6 +889,9 @@ public class AddOnManager {
                                     } catch (Exception e) {
                                         // TODO: handle exception
                                     }
+                                    if (loadedClass == null && packageNameAndVersion != null) {
+                                    	classToPackageMap.put(className, packageNameAndVersion);
+                                    }
                                 }
                             }
                             jarFile.close();
@@ -1376,7 +1395,7 @@ public class AddOnManager {
 
         // Define headers here - need to know lengths
         String nameHeader = "Name";
-        String statusHeader = "Installation Status";
+        String statusHeader = "Installed Version";
         String latestHeader = "Latest Version";
         String depsHeader = "Dependencies";
         String descriptionHeader = "Description";
@@ -1395,7 +1414,7 @@ public class AddOnManager {
             packageList.add(pkg);
 
             maxNameWidth = Math.max(pkg.getName().length(), maxNameWidth);
-            maxStatusWidth = Math.max(pkg.getStatusString().length(), maxStatusWidth);
+            maxStatusWidth = Math.max(pkg.isInstalled() ? pkg.getInstalledVersion().toString().length() : 2, maxStatusWidth);
             maxLatestWidth = Math.max(maxLatestWidth, pkg.isAvailable()
                             ? pkg.getLatestVersion().toString().length()
                             :  Math.max(2, maxStatusWidth));
@@ -1429,7 +1448,7 @@ public class AddOnManager {
         for (Package pkg : packageList) {
         	if (pkg.getName().equals(BEAST_PACKAGE_NAME)) {
         		ps.printf(nameFormat, pkg.getName()); ps.print(sep);
-		        ps.printf(statusFormat, pkg.getStatusString()); ps.print(sep);
+		        ps.printf(statusFormat, pkg.isInstalled() ? pkg.getInstalledVersion() : "NA"); ps.print(sep);
 		        ps.printf(latestFormat, pkg.isAvailable() ? pkg.getLatestVersion() : "NA"); ps.print(sep);
 		        ps.printf(depsFormat, pkg.getDependenciesString()); ps.print(sep);
 		        ps.printf("%s\n", pkg.getDescription());
@@ -1443,7 +1462,7 @@ public class AddOnManager {
         for (Package pkg : packageList) {
         	if (!pkg.getName().equals(BEAST_PACKAGE_NAME)) {
 	            ps.printf(nameFormat, pkg.getName()); ps.print(sep);
-	            ps.printf(statusFormat, pkg.getStatusString()); ps.print(sep);
+	            ps.printf(statusFormat, pkg.isInstalled() ? pkg.getInstalledVersion() : "NA"); ps.print(sep);
 	            ps.printf(latestFormat, pkg.isAvailable() ? pkg.getLatestVersion() : "NA"); ps.print(sep);
 	            ps.printf(depsFormat, pkg.getDependenciesString()); ps.print(sep);
 	            ps.printf("%s\n", pkg.getDescription());
@@ -1578,4 +1597,58 @@ public class AddOnManager {
             e.printStackTrace();
         }
     }
+
+    /** keep track of which class comes from a particular package.
+     * It maps a full class name onto a package name + " v" + package version
+     * e.g. "bModelTest v0.3.2"
+     */
+    private static Map<String, String> classToPackageMap = new HashMap<>();
+    
+    /**  maps package name to a Package object, which contains info on whether 
+     * and which version is installed. This is initialised when loadExternalJars()
+     * is called, which happens at the start of BEAST, BEAUti and many utilities.
+     */
+    private static TreeMap<String, Package> packages = new TreeMap<>();
+
+    /** return set of Strings in the format of classToPackageMap (like "bModelTest v0.3.2")
+     * for all packages used by o and its predecessors in the model graph.
+     */
+    public static Set<String> getPackagesAndVersions(BEASTInterface o) {
+    	Set<String> packagesAndVersions = new LinkedHashSet<>();
+    	getPackagesAndVersions(o, packagesAndVersions);
+    	return packagesAndVersions;
+    }
+    
+    /** traverse model graph starting at o, and collect packageAndVersion strings
+     * along the way.
+     */
+    private static void getPackagesAndVersions(BEASTInterface o, Set<String> packagesAndVersions) {
+    	String packageAndVersion = classToPackageMap.get(o.getClass().getName());
+    	if (packageAndVersion != null) {
+    		packagesAndVersions.add(packageAndVersion);
+    	}
+    	for (BEASTInterface o2 : o.listActiveBEASTObjects()) {
+    		getPackagesAndVersions(o2, packagesAndVersions);
+    	}
+	}
+
+    /** test whether a package with given name and version is available.
+     * @param pkgname
+     * @param pkgversion ignored for now
+     * @return
+     */
+    // RRB: may need to return PackageStatus instead of boolean, but not sure yet how to handle
+    // the case where a newer package is installed, and the old one is not available yet.
+    //public static enum PackageStatus {NOT_INSTALLED, INSTALLED, INSTALLED_VERSION_NOT_AVAILABLE};
+    public static boolean isInstalled(String pkgname, String pkgversion) {
+		if (!packages.containsKey(pkgname)) {
+			return false;
+		}
+		return true;
+//		Package pkg = packages.get(pkgname);
+//		PackageVersion version = new PackageVersion(pkgversion);
+//		if (pkg.isAvailable(version)) {
+//			return false;
+//		}
+	}
  }
diff --git a/src/beast/util/Package.java b/src/beast/util/Package.java
index 3c95e0c..ff737ce 100644
--- a/src/beast/util/Package.java
+++ b/src/beast/util/Package.java
@@ -22,6 +22,8 @@ public class Package {
     protected TreeMap<PackageVersion, URL> availableVersionURLs;
     protected TreeMap<PackageVersion, Set<PackageDependency>> availableVersionDeps;
 
+    protected URL projectURL;
+
     public Package(String name) {
         this.packageName = name;
         this.description = "";
@@ -42,6 +44,14 @@ public class Package {
         this.description = description;
     }
 
+    public URL getProjectURL() {
+        return projectURL;
+    }
+
+    public void setProjectURL(URL url) {
+        this.projectURL = url;
+    }
+
     /**
      * @return true iff package is available online.
      */
@@ -87,10 +97,6 @@ public class Package {
         return installedVersionDeps;
     }
 
-    public String getStatusString() {
-        return isInstalled() ? installedVersion.getVersionString() : NOT_INSTALLED;
-    }
-
     /**
      * @return latest available version of package.
      */
@@ -193,7 +199,7 @@ public class Package {
     public String toHTML() {
         String html = "<html>";
         html += "<h1>" + packageName + "</h1>";
-        html += "<p>Installed version: " + getStatusString() + "</p>";
+        html += "<p>Installed version: " + (isInstalled() ? getInstalledVersion() : "NA") + "</p>";
         html += "<p>Latest version: " + (isAvailable() ? getLatestVersion() : "NA") + "</p>";
         html += "<p>" + description +"</p>";
         html += "</html>";
diff --git a/src/beast/util/TreeParser.java b/src/beast/util/TreeParser.java
index daa7cea..b46423e 100644
--- a/src/beast/util/TreeParser.java
+++ b/src/beast/util/TreeParser.java
@@ -467,8 +467,19 @@ public class TreeParser extends Tree implements StateNodeInitialiser {
                                 stringValue = stringValue.substring(1, stringValue.length()-1);
                             }
                             node.setMetaData(key, stringValue);
-                        } else {
-                            // BEAST doesn't do anything with vectors yet.
+                        } else if (attribctx.attribValue().vector() != null) {
+                        	try {
+	                        	String value = attribctx.attribValue().vector().getText();
+								String str = value.substring(1, value.length() - 1); 
+								String [] strs = str.split(",");
+								Double [] values = new Double[strs.length];
+								for (int j = 0; j < strs.length; j++) {
+									values[j] = Double.parseDouble(strs[j]); 
+								}
+								node.setMetaData(key, values);
+                        	} catch (Exception e) {
+                        		// ignore parsing errors
+                        	}
                         }
                     }
                 }
diff --git a/src/beast/util/XMLParser.java b/src/beast/util/XMLParser.java
index 46acc9d..7b59b31 100644
--- a/src/beast/util/XMLParser.java
+++ b/src/beast/util/XMLParser.java
@@ -196,6 +196,7 @@ public class XMLParser {
     HashMap<String, BEASTInterface> IDMap;
     HashMap<String, Integer[]> likelihoodMap;
     HashMap<String, Node> IDNodeMap;
+    String unavailablePacakges = "";
 
 
     static HashMap<String, String> element2ClassMap;
@@ -332,7 +333,7 @@ public class XMLParser {
 //        if (typeName == null || !typeName.equals("template")) {
 //        	return beastObjects;
 //        }
-
+        // sanity check that required packages are installed
 
         initIDNodeMap(topNode);
         parseNameSpaceAndMap(topNode);
@@ -485,6 +486,31 @@ public class XMLParser {
             throw new XMLParserException(topNode, "Wrong version: only versions > 2.0 are supported", 101);
         }
 
+        String required = getAttribute(topNode, "required");
+        if (required != null && required.trim().length() > 0) {
+        	String [] packageAndVersions = required.split(":");
+        	for (String s : packageAndVersions) {
+        		s = s.trim();
+        		int i = s.lastIndexOf(" ");
+        		if (i > 0) {
+        			String pkgname = s.substring(0, i);
+        			String pkgversion = s.substring(i+1);
+        			if (!AddOnManager.isInstalled(pkgname, pkgversion)) {
+        				unavailablePacakges += s +", ";
+        			}
+        		}
+        	}
+        	if (unavailablePacakges.length() > 1) {
+        		unavailablePacakges = unavailablePacakges.substring(0, unavailablePacakges.length() - 2);
+        		if (unavailablePacakges.contains(",")) {
+        			Log.warning("The following packages are required, but not available: " + unavailablePacakges);
+        		} else {
+        			Log.warning("The following package is required, but is not available: " + unavailablePacakges);
+        		}
+        		Log.warning("See http://beast2.org/managing-packages/ for details on how to install packages.");
+        	}
+        }
+
         initIDNodeMap(topNode);
         parseNameSpaceAndMap(topNode);
 
@@ -713,6 +739,15 @@ public class XMLParser {
             }
 		}
 		if (clazzName == null) {
+			if (unavailablePacakges.length() > 2) {
+				String msg = "Class " + specClass + " could not be found.\n" +
+		        		(unavailablePacakges.contains(",") ?
+							"This XML requires the following packages that are not installed: " :
+							"This XML requires the following package that is not installed: ") + unavailablePacakges + "\n" +
+		        		"See http://beast2.org/managing-packages/ for details on how to install packages.\n" +
+						"Or perhaps there is a typo in spec and you meant " + XMLParserUtils.guessClass(specClass) + "?";
+				throw new XMLParserException(node, msg, 1018);				
+			}
 			throw new XMLParserException(node, "Class could not be found. Did you mean " + XMLParserUtils.guessClass(specClass) + "?", 1017);
 			// throw new ClassNotFoundException(specClass);
 		}
diff --git a/src/beast/util/XMLParserUtils.java b/src/beast/util/XMLParserUtils.java
index 6bd272a..de334d4 100644
--- a/src/beast/util/XMLParserUtils.java
+++ b/src/beast/util/XMLParserUtils.java
@@ -28,6 +28,7 @@ import beast.core.BEASTInterface;
 import beast.core.Input;
 import beast.core.InputForAnnotatedConstructor;
 import beast.core.Param;
+import beast.core.util.Log;
 
 /**
  *
@@ -73,12 +74,18 @@ public class XMLParserUtils {
             List<String> vals = new ArrayList<>();
             for (final String valueString : valuesString) {
                 if (valueString.indexOf(":") > 0) {
-                    String[] range = valueString.split(":");
-                    int min = Integer.parseInt(range[0]);
-                    int max = Integer.parseInt(range[1]);
-                    for (int i = min; i <= max; i++) {
-                        vals.add(String.valueOf(i));
-                    }
+                	try {
+	                    String[] range = valueString.split(":");
+	                    int min = Integer.parseInt(range[0]);
+	                    int max = Integer.parseInt(range[1]);
+	                    for (int i = min; i <= max; i++) {
+	                        vals.add(String.valueOf(i));
+	                    }
+                	} catch (NumberFormatException e) {
+                		Log.warning.println("plate range value '" + valueString + "'contains a ':' but does not seem to be a range, (like 1:5).");
+                		Log.warning.println("interpreting it as if it were not a range");
+                        vals.add(valueString);
+                	}
                 } else {
                     vals.add(valueString);
                 }
diff --git a/src/beast/util/XMLProducer.java b/src/beast/util/XMLProducer.java
index 6af6b7d..0d455f8 100644
--- a/src/beast/util/XMLProducer.java
+++ b/src/beast/util/XMLProducer.java
@@ -56,6 +56,7 @@ import org.w3c.dom.NodeList;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
+import beast.app.BEASTVersion2;
 import beast.core.BEASTInterface;
 import beast.core.Input;
 
@@ -98,7 +99,14 @@ public class XMLProducer extends XMLParser {
     public String toXML(BEASTInterface beastObject, Collection<BEASTInterface> others) {
         try {
             StringBuffer buf = new StringBuffer();
-            buf.append("<" + XMLParser.BEAST_ELEMENT + " version='2.0' namespace='" + DEFAULT_NAMESPACE + "'>\n");
+        	Set<String> requiredPacakges = AddOnManager.getPackagesAndVersions(beastObject);
+        	String required = requiredPacakges.toString();
+        	required = required.substring(1, required.length() - 1);
+        	required = required.replaceAll(", ", ":");
+            buf.append("<" + XMLParser.BEAST_ELEMENT + 
+            		" version='" + new BEASTVersion2().getMajorVersion() + "'" +
+            		" required='" + required + "'" +
+            		" namespace='" + DEFAULT_NAMESPACE + "'>\n");
             for (String element : element2ClassMap.keySet()) {
             	if (!reservedElements.contains(element)) {
             		buf.append("<map name='" + element + "'>" + element2ClassMap.get(element) +"</map>\n");
@@ -826,8 +834,24 @@ public class XMLProducer extends XMLParser {
 
         // open element
         buf.append("<").append(elementName);
+        
+        if (beastObject.getID() == null) {
+        	String id = beastObject.getClass().getName();
+        	if (id.contains(".")) {
+        		id = id.substring(id.lastIndexOf('.') + 1);
+        	}
+            if (IDs.contains(id)) {
+                int k = 1;
+                while (IDs.contains(id + k)) {
+                    k++;
+                }
+                id = id + k;
+            }
+            beastObject.setID(id);
+        }
 
         boolean skipInputs = false;
+        // isDone.contains(beastObject) fails when BEASTObjects override equals(), so use a stream with == instead
         if (isDone.stream().anyMatch(x -> x == beastObject)) {
             // XML is already produced, we can idref it
             buf.append(" idref='" + normalise(beastObject.getID()) + "'");
diff --git a/src/test/beast/app/beauti/BeautiRateTutorialTest.java b/src/test/beast/app/beauti/BeautiRateTutorialTest.java
index e2d8d5d..5774e6a 100644
--- a/src/test/beast/app/beauti/BeautiRateTutorialTest.java
+++ b/src/test/beast/app/beauti/BeautiRateTutorialTest.java
@@ -236,8 +236,9 @@ public class BeautiRateTutorialTest extends BeautiBase {
 		warning("7. Run MCMC and look at results in Tracer, TreeAnnotator->FigTree");
 		makeSureXMLParses();
 		
- 		MEPRunner runner = new MEPRunner(org.fest.util.Files.temporaryFolder());
- 		runner.analyse(0);
+		// TODO: this should run as a separate process since the BEAUti run can interfere with the BEAST run on Hudson
+ 		//MEPRunner runner = new MEPRunner(org.fest.util.Files.temporaryFolder());
+ 		//runner.analyse(0);
 		
 		long t1 = System.currentTimeMillis();
 		System.err.println("total time: " + (t1 - t0)/1000 + " seconds");
diff --git a/src/test/beast/core/BEASTInterfaceTest.java b/src/test/beast/core/BEASTInterfaceTest.java
index 1b5e5c5..5f667e3 100644
--- a/src/test/beast/core/BEASTInterfaceTest.java
+++ b/src/test/beast/core/BEASTInterfaceTest.java
@@ -58,6 +58,12 @@ public class BEASTInterfaceTest extends TestCase {
 		}
 	}
 	
+	@Description("class that extends BEASTi with multiple citations")
+	@Citation("this is another dummy citation")
+	@Citation("and yet another dummy citation")
+	public class BEASTi2 extends BEASTi {
+		
+	}
 	
 	@Test
 	public void testBEASTi() throws Exception {
@@ -67,6 +73,14 @@ public class BEASTInterfaceTest extends TestCase {
 		Citation citation = beasti.getCitation();
 		assertEquals("this is a dummy citation", citation.value());
 
+		citation = beasti.getCitationList().get(0);
+		assertEquals("this is a dummy citation", citation.value());
+		
+		BEASTi beasti02 = new BEASTi2();
+		List<Citation> citations = beasti02.getCitationList();
+		assertEquals(3, citations.size());
+
+
 		System.err.println("test initByName");
 		beasti.initByName("value", "hello world");
 		Input<?> input = beasti.getInput("value");
@@ -127,6 +141,8 @@ public class BEASTInterfaceTest extends TestCase {
 		beasti2.setInputValue("value", "Goodbye!");
 		String msg = (String) beasti2.getInputValue("value");
 		assertEquals("Goodbye!", msg);
+	
+		
 		
 	}
 	
diff --git a/src/test/beast/core/util/SumTest.java b/src/test/beast/core/util/SumTest.java
new file mode 100644
index 0000000..f566136
--- /dev/null
+++ b/src/test/beast/core/util/SumTest.java
@@ -0,0 +1,63 @@
+package test.beast.core.util;
+
+import org.junit.Test;
+
+import beast.core.parameter.BooleanParameter;
+import beast.core.parameter.IntegerParameter;
+import beast.core.parameter.RealParameter;
+import beast.core.util.Sum;
+import junit.framework.TestCase;
+
+public class SumTest extends TestCase {
+
+	
+	@Test
+	public void testSum() {
+		RealParameter p1 = new RealParameter("1.0 2.0");
+		Sum sum = new Sum();
+		
+		// single argument sum
+		sum.initByName("arg", p1);
+		double v = sum.getArrayValue();
+		assertEquals(3.0, v, 1e-10);
+		
+		// multiple argument sum
+		sum = new Sum();
+		RealParameter p2 = new RealParameter("2.0 2.5");
+		sum.initByName("arg", p1, "arg", p2);
+		v = sum.getArrayValue();
+		assertEquals(7.5, v, 1e-10);
+
+		// multiple same argument sum
+		sum = new Sum();
+		sum.initByName("arg", p1, "arg", p1);
+		v = sum.getArrayValue();
+		assertEquals(6.0, v, 1e-10);
+		
+		// sum of integers
+		IntegerParameter p3 = new IntegerParameter("1 2 5");
+		sum = new Sum();
+		sum.initByName("arg", p3);
+		v = sum.getArrayValue();
+		assertEquals(8.0, v, 1e-10);
+
+		// sum of boolean
+		BooleanParameter p4 = new BooleanParameter("true false false true true");
+		sum = new Sum();
+		sum.initByName("arg", p4);
+		v = sum.getArrayValue();
+		assertEquals(3.0, v, 1e-10);
+
+		// sum of booleans and integer
+		sum = new Sum();
+		sum.initByName("arg", p4, "arg", p3);
+		v = sum.getArrayValue();
+		assertEquals(11.0, v, 1e-10);
+
+		// sum of booleans and real
+		sum = new Sum();
+		sum.initByName("arg", p1, "arg", p4);
+		v = sum.getArrayValue();
+		assertEquals(6.0, v, 1e-10);
+	}
+}
diff --git a/src/test/beast/evolution/datatype/IntegerDataTest.java b/src/test/beast/evolution/datatype/IntegerDataTest.java
new file mode 100644
index 0000000..c595704
--- /dev/null
+++ b/src/test/beast/evolution/datatype/IntegerDataTest.java
@@ -0,0 +1,32 @@
+package test.beast.evolution.datatype;
+
+import org.junit.Test;
+
+import beast.evolution.datatype.IntegerData;
+import beast.util.Randomizer;
+import junit.framework.TestCase;;
+
+public class IntegerDataTest extends TestCase {
+
+	
+	@Test
+	public void testIntegerData() {
+		IntegerData datatype = new IntegerData();
+		assertEquals("?", datatype.getCode(-1));
+		assertEquals("0", datatype.getCode(0));
+		assertEquals("1", datatype.getCode(1));
+		assertEquals("10", datatype.getCode(10));
+		assertEquals("123", datatype.getCode(123));
+		Randomizer.setSeed(127);
+		for (int i = 0; i < 100; i++) {
+			int state = Randomizer.nextInt(100000000);
+			int x = state;
+	    	String str = "";
+	    	while (state > 0) {
+	    		str = (char)('0' + state%10) + str;
+	    		state /= 10;
+	    	}
+			assertEquals(str, datatype.getCode(x));
+		}
+	}
+}
diff --git a/src/test/beast/evolution/tree/TraitSetTest.java b/src/test/beast/evolution/tree/TraitSetTest.java
new file mode 100644
index 0000000..055fba0
--- /dev/null
+++ b/src/test/beast/evolution/tree/TraitSetTest.java
@@ -0,0 +1,78 @@
+/**
+ * 
+ */
+package test.beast.evolution.tree;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import beast.evolution.alignment.Alignment;
+import beast.evolution.alignment.Sequence;
+import beast.evolution.alignment.TaxonSet;
+import beast.evolution.tree.TraitSet;
+
+/**
+ * @author Gereon Kaiping <g.a.kaiping at hum.leidenuniv.nl>
+ *
+ */
+public class TraitSetTest {
+	
+	public TaxonSet taxonSet(int Nleaves) {
+    List<Sequence> seqList = new ArrayList<Sequence>();
+
+    for (int i=0; i<Nleaves; i++) {
+        String taxonID = "t" + i;
+        seqList.add(new Sequence(taxonID, "?"));
+    }
+
+    Alignment alignment = new Alignment(seqList, "nucleotide");
+    TaxonSet taxonSet = new TaxonSet(alignment);
+    return taxonSet;}
+	
+
+	@Test
+	public void testDateBackward() {
+		int Nleaves = 2; 
+	    TraitSet timeTrait = new TraitSet();
+
+	    timeTrait.initByName(
+	            "traitname", "date-backward",
+	            "taxa", taxonSet(Nleaves),
+	            "value", "t0=0, t1=10");
+	    // The trait actually represents the age of the taxa relative to
+	    // each other with arbitrary zero, so we test it like this.
+	    assertEquals(-10.0, timeTrait.getValue("t0")-timeTrait.getValue("t1"), 1e-7);
+	}
+
+	@Test
+	public void testDateForward() {
+		int Nleaves = 2; 
+	    TraitSet timeTrait = new TraitSet();
+
+	    timeTrait.initByName(
+	            "traitname", "date-forward",
+	            "taxa", taxonSet(Nleaves),
+	            "value", "t0=0, t1=10");
+	    // The trait actually represents the age of the taxa relative to
+	    // each other with arbitrary zero, so we test it like this.
+	    assertEquals(10.0, timeTrait.getValue("t0")-timeTrait.getValue("t1"), 1e-7);
+	}
+
+	@Test
+	public void testDateForwardUnspecified() {
+		int Nleaves = 2; 
+	    TraitSet timeTrait = new TraitSet();
+
+	    timeTrait.initByName(
+	            "traitname", "date-forward",
+	            "taxa", taxonSet(Nleaves),
+	            "value", "t1=10");
+	    // The trait actually represents the age of the taxa relative to
+	    // each other with arbitrary zero, so we test it like this.
+	    assertEquals(10.0, timeTrait.getValue("t0")-timeTrait.getValue("t1"), 1e-7);
+	}
+}
diff --git a/src/test/beast/math/distributions/GammaTest.java b/src/test/beast/math/distributions/GammaTest.java
index 6dd7531..2164519 100644
--- a/src/test/beast/math/distributions/GammaTest.java
+++ b/src/test/beast/math/distributions/GammaTest.java
@@ -1,8 +1,16 @@
 package test.beast.math.distributions;
 
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.integration.RombergIntegrator;
+import org.apache.commons.math.analysis.integration.UnivariateRealIntegrator;
 import org.junit.Test;
 
+import beast.math.GammaFunction;
 import beast.math.distributions.Gamma;
+import beast.util.Randomizer;
 import junit.framework.TestCase;
 
 public class GammaTest extends TestCase {
@@ -25,4 +33,97 @@ public class GammaTest extends TestCase {
 		
 	}
 
+	/** The code below is adapted from GammaDistributionTest from BEAST 1
+	 * This test stochastically draws gamma
+	 * variates and compares the coded pdf 
+	 * with the actual pdf.  
+	 * The tolerance is required to be at most 1e-10.
+	 */
+
+    static double mypdf(double value, double shape, double scale) {
+        return Math.exp((shape-1) * Math.log(value) - value/scale - GammaFunction.lnGamma(shape) - shape * Math.log(scale) );
+    }
+
+	public void testPdf() throws MathException  {
+
+        final int numberOfTests = 300;
+        double totErr = 0;
+        double ptotErr = 0; int np = 0;
+        double qtotErr = 0;
+
+        Randomizer.setSeed(551);
+
+        for(int i = 0; i < numberOfTests; i++){
+            final double mean = .01 + (3-0.01) * Randomizer.nextDouble();
+            final double var = .01 + (3-0.01) * Randomizer.nextDouble();
+
+            double scale0 = var / mean;
+            double shape = mean / scale0;
+
+            final Gamma gamma = new Gamma();
+            Gamma.mode mode = Gamma.mode.values()[Randomizer.nextInt(4)];
+            
+            double other = 0;
+        	switch (mode) {
+        	case ShapeScale: other = scale0; break;
+        	case ShapeRate: other = 1/scale0; break;
+        	case ShapeMean: other = scale0 * shape; break;
+        	case OneParameter: other = 1/shape; scale0 = 1/shape; break;
+        	}
+            final double scale = scale0;
+
+            gamma.initByName("alpha", shape +"", "beta", other +"", "mode", mode);
+
+            final double value = Randomizer.nextGamma(shape, 1/scale);
+
+            final double mypdf = mypdf(value, shape, scale);
+            final double pdf = gamma.density(value);
+            if ( Double.isInfinite(mypdf) && Double.isInfinite(pdf)) {
+                continue;
+            }
+
+            assertFalse(Double.isNaN(mypdf));
+            assertFalse(Double.isNaN(pdf));
+
+            totErr +=  mypdf != 0 ? Math.abs((pdf - mypdf)/mypdf) : pdf;
+
+            assertFalse("nan", Double.isNaN(totErr));
+            //assertEquals("" + shape + "," + scale + "," + value, mypdf,gamma.pdf(value),1e-10);
+
+            final double cdf = gamma.cumulativeProbability(value);
+            UnivariateRealFunction f = new UnivariateRealFunction() {
+                public double value(double v) throws FunctionEvaluationException {
+                    return mypdf(v, shape, scale);
+                }
+            };
+            final UnivariateRealIntegrator integrator = new RombergIntegrator();
+            integrator.setAbsoluteAccuracy(1e-14);
+            integrator.setMaximalIterationCount(16);  // fail if it takes too much time
+
+            double x;
+            try {
+                x = integrator.integrate(f, 0, value);
+                ptotErr += cdf != 0.0 ? Math.abs(x-cdf)/cdf : x;
+                np += 1;
+                //assertTrue("" + shape + "," + scale + "," + value + " " + Math.abs(x-cdf)/x + "> 1e-6", Math.abs(1-cdf/x) < 1e-6);
+
+                final double q = gamma.inverseCumulativeProbability(cdf);
+                qtotErr += q != 0 ? Math.abs(q-value)/q : value;
+                //System.out.println(shape + ","  + scale + " " + value);
+            } catch( ConvergenceException e ) {
+                 // can't integrate , skip test
+                 //System.out.print(" theta(" + shape + ","  + scale + ") skipped");
+            }
+
+           // assertEquals("" + shape + "," + scale + "," + value + " " + Math.abs(q-value)/value, q, value, 1e-6);
+           // System.out.print("\n" + np + ": " + mode + " " + totErr/np + " " + qtotErr/np + " " + ptotErr/np);
+        }
+        //System.out.println( !Double.isNaN(totErr) );
+       // System.out.println(totErr);
+        // bad test, but I can't find a good threshold that works for all individual cases 
+        assertTrue("failed " + totErr/numberOfTests, totErr/numberOfTests < 1e-7);
+        assertTrue("failed " + qtotErr/numberOfTests , qtotErr/numberOfTests < 1e-10);
+        assertTrue("failed " + ptotErr/np, np > 0 ? (ptotErr/np < 2e-7) : true);
+	}
+
 }
diff --git a/src/test/beast/math/distributions/InvGammaTest.java b/src/test/beast/math/distributions/InvGammaTest.java
new file mode 100644
index 0000000..545cdfc
--- /dev/null
+++ b/src/test/beast/math/distributions/InvGammaTest.java
@@ -0,0 +1,128 @@
+package test.beast.math.distributions;
+
+import org.apache.commons.math.MathException;
+
+import beast.math.distributions.InverseGamma;
+import junit.framework.TestCase;
+
+/**
+ * Simple test for inverse gamma distribution.
+ *
+ * @author Joseph Heled
+ *         Date: 24/04/2009
+ */
+public class InvGammaTest extends TestCase {
+    interface TestData {
+        double getShape();
+
+        double getScale();
+
+        double[] getPDF();
+
+        double[] getCDF();
+    }
+
+    // test data generated from this python code:
+// import scipy.stats
+//
+//print """TestData[] tests = {"""
+//for shape,scale in ((3,2), (3,1)) :
+//  d = scipy.stats.invgamma(shape, scale=scale)
+//  print """
+//        new TestData() {
+//            public double getShape() {
+//                return %d;
+//            }
+//
+//            public double getScale() {
+//               return %d;
+//            }""" % (shape, scale)
+//  x = (0.5, 1, 2)
+//  print """
+//            public double[] getPDF() {
+//                return new double[]{%s};
+//            }""" % " , ".join(["%g,%.14lf" % (z,d.pdf(z)) for z in x])
+//
+//  print """
+//            public double[] getCDF() {
+//                return new double[]{%s};
+//            }
+//        } ,""" % " , ".join(["%g,%.14lf" % (z,d.cdf(z)) for z in x])
+//
+
+    TestData[] tests = {
+
+            new TestData() {
+                public double getShape() {
+                    return 3;
+                }
+
+                public double getScale() {
+                    return 2;
+                }
+
+                public double[] getPDF() {
+                    return new double[]{0.5, 1.17220088887899, 1, 0.54134113294645, 2, 0.09196986029286};
+                }
+
+                public double[] getCDF() {
+                    return new double[]{0.5, 0.23810330555354, 1, 0.67667641618306, 2, 0.91969860292861};
+                }
+            },
+
+            new TestData() {
+                public double getShape() {
+                    return 3;
+                }
+
+                public double getScale() {
+                    return 1;
+                }
+
+                public double[] getPDF() {
+                    return new double[]{0.5, 1.08268226589290, 1, 0.18393972058572, 2, 0.01895408311602};
+                }
+
+                public double[] getCDF() {
+                    return new double[]{0.5, 0.67667641618306, 1, 0.91969860292861, 2, 0.98561232203303};
+                }
+            },
+    };
+
+
+    public void testInvGamma() throws MathException {
+        for( TestData td : tests ) {
+            InverseGamma d = new InverseGamma();
+            d.initByName("alpha", td.getShape() + "" , "beta", td.getScale() + "");
+
+            {
+                double[] p = td.getPDF();
+                for(int k = 0; k < p.length; k += 2) {
+                    assertEquals(d.density(p[k]), p[k + 1], 1e-10);
+
+                    assertEquals(d.logDensity(p[k]), Math.log(p[k + 1]), 1e-10);
+                }
+            }
+
+            double[] cdf = td.getCDF();
+            for(int k = 0; k < cdf.length; k += 2) {
+            	// InverseGamma.cumulativeProbability is not implemented yet
+ //               assertEquals(d.cumulativeProbability(cdf[k]), cdf[k + 1], 1e-10);
+            }
+
+//            int count[] = new int[cdf.length];
+//            final int N = 100000;
+//            for(int k = 0; k < N; ++k) {
+//                double x = d.nextInverseGamma();
+//                for(int l = 0; l < cdf.length; l += 2) {
+//                    if( x < cdf[l] ) {
+//                        count[l / 2] += 1;
+//                    }
+//                }
+//            }
+//            for(int l = 0; l < cdf.length; l += 2) {
+//                assertEquals(count[l / 2] / (double) N, cdf[l + 1], 5e-3);
+//            }
+        }
+    }
+}
diff --git a/src/test/beast/math/distributions/LogNormalDistributionModelTest.java b/src/test/beast/math/distributions/LogNormalDistributionModelTest.java
index 39cde57..6ad4926 100644
--- a/src/test/beast/math/distributions/LogNormalDistributionModelTest.java
+++ b/src/test/beast/math/distributions/LogNormalDistributionModelTest.java
@@ -1,10 +1,12 @@
 package test.beast.math.distributions;
 
 
+import org.apache.commons.math.MathException;
 import org.junit.Test;
 
 import beast.core.parameter.RealParameter;
 import beast.math.distributions.LogNormalDistributionModel;
+import beast.util.Randomizer;
 import beast.util.XMLParser;
 import junit.framework.TestCase;
 
@@ -17,12 +19,12 @@ public class LogNormalDistributionModelTest extends TestCase {
         logNormal.init("1.0", "2.0");
 
         for (int i = 0; i < 10000; i++) {
-            double M = Math.random() * 10.0 - 5.0;
-            double S = Math.random() * 10;
+            double M = Randomizer.nextDouble() * 10.0 - 5.0;
+            double S = Randomizer.nextDouble() * 10;
 
             double x = -1;
             while( x < 0 ) {
-                x = Math.log(Math.random() * 10);
+                x = Math.log(Randomizer.nextDouble() * 10);
             }
 
             logNormal.MParameterInput.setValue(M + "", logNormal);
@@ -79,5 +81,137 @@ public class LogNormalDistributionModelTest extends TestCase {
         double f0 = logNormal.calcLogP(p);
         assertEquals(-7.880210654973873, f0, 1e-10);
     }
+
+
+    
+    // remainder is adapted from Alexei's LogNormalDistributionTest from BEAST 1
+    LogNormalDistributionModel logNormal;
+
+    public void setUp() {
+
+        logNormal = new LogNormalDistributionModel();
+        logNormal.initByName("M", "1.0", "S", "2.0");
+        Randomizer.setSeed(123);
+    }
+
+    public void testPdf() {
+
+        System.out.println("Testing 10000 random pdf calls");
+
+        for (int i = 0; i < 10000; i++) {
+            double M = Randomizer.nextDouble() * 10.0 - 5.0;
+            double S = Randomizer.nextDouble() * 10;
+
+            double x = Math.log(Randomizer.nextDouble() * 10);
+
+            logNormal.MParameterInput.setValue(M + "", logNormal);
+            logNormal.SParameterInput.setValue(S + "", logNormal);
+            logNormal.initAndValidate();
+
+            double pdf = 1.0 / (x * S * Math.sqrt(2 * Math.PI)) * Math.exp(-Math.pow(Math.log(x) - M, 2) / (2 * S * S));
+            if (x <= 0) pdf = 0; // see logNormal.pdf(x)
+
+            //System.out.println("Testing logNormal[M=" + M + " S=" + S + "].pdf(" + x + ")");
+
+            assertEquals(pdf, logNormal.density(x), 1e-10);
+        }
+    }
+
+    public void testMean() {
+
+        for (int i = 0; i < 1000; i++) {
+            double M = Randomizer.nextDouble() * 10.0 - 5.0;
+            double S = Randomizer.nextDouble() * 10;
+
+            logNormal.MParameterInput.setValue(M + "", logNormal);
+            logNormal.SParameterInput.setValue(S + "", logNormal);
+            logNormal.initAndValidate();
+            
+            double mean = Math.exp(M + S * S / 2);
+
+            //System.out.println("Testing logNormal[M=" + M + " S=" + S + "].mean()");
+
+            assertEquals(mean, logNormal.getMean(), 1e-10);
+        }
+    }
+
+//    public void testVariance() {
+//
+//        for (int i = 0; i < 1000; i++) {
+//            double M = Randomizer.nextDouble() * 10.0 - 5.0;
+//            double S = Randomizer.nextDouble() * 10;
+//
+//            logNormal.MParameterInput.setValue(M, logNormal);
+//            logNormal.SParameterInput.setValue(S, logNormal);
+//            logNormal.initAndValidate();
+//
+//            double variance = (Math.exp(S * S) - 1) * Math.exp(2 * M + S * S);
+//
+//            //System.out.println("Testing logNormal[M=" + M + " S=" + S + "].variance()");
+//
+//            assertEquals(variance, logNormal.getVariance(), 1e-10);
+//        }
+//    }
+
+
+    public void testMedian() throws MathException {
+
+        System.out.println("Testing 10000 random quantile(0.5) calls");
+
+        for (int i = 0; i < 10000; i++) {
+            double M = Randomizer.nextDouble() * 10.0 - 5.0;
+            double S = Randomizer.nextDouble() * 10;
+
+            logNormal.MParameterInput.setValue(M + "", logNormal);
+            logNormal.SParameterInput.setValue(S + "", logNormal);
+            logNormal.initAndValidate();
+
+            double median = Math.exp(M);
+
+            //System.out.println("Testing logNormal[M=" + M + " S=" + S + "].median()");
+
+            assertEquals(median, logNormal.inverseCumulativeProbability(0.5), median / 1e6);
+        }
+    }
+
+    public void testCDFAndQuantile() throws MathException {
+
+        System.out.println("Testing 10000 random quantile/cdf pairs");
+
+        for (int i = 0; i < 10000; i++) {
+
+            double M = Randomizer.nextDouble() * 10.0 - 5.0;
+            double S = Randomizer.nextDouble() * 10;
+
+            logNormal.MParameterInput.setValue(M + "", logNormal);
+            logNormal.SParameterInput.setValue(S + "", logNormal);
+            logNormal.initAndValidate();
+
+            double p = Randomizer.nextDouble();
+            double quantile = logNormal.inverseCumulativeProbability(p);
+
+            double cdf = logNormal.cumulativeProbability(quantile);
+
+            assertEquals(p, cdf, 1e-7);
+        }
+    }
+
+//    public void testCDFAndQuantile2() {
+//
+//        final LogNormalDistributionModel f = new LogNormalDistributionModel();
+//        logNormal.initByName("M", "1.0", "S", "1.0");
+//        for (double i = 0.01; i < 0.95; i += 0.01) {
+//            final double y = i;
+//
+//            BisectionZeroFinder zeroFinder = new BisectionZeroFinder(new OneVariableFunction() {
+//                public double value(double x) {
+//                    return f.cdf(x) - y;
+//                }
+//            }, 0.01, 100);
+//            zeroFinder.evaluate();
+//
+//            assertEquals(f.quantile(i), zeroFinder.getResult(), 1e-6);
+//        }
+//    }
 }
 
diff --git a/src/test/beast/math/distributions/MRCAPriorTest.java b/src/test/beast/math/distributions/MRCAPriorTest.java
index f15bbf7..39c4e4a 100644
--- a/src/test/beast/math/distributions/MRCAPriorTest.java
+++ b/src/test/beast/math/distributions/MRCAPriorTest.java
@@ -1,5 +1,9 @@
 package test.beast.math.distributions;
 
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+
 import org.junit.Test;
 
 import beast.evolution.alignment.Alignment;
@@ -64,6 +68,21 @@ public class MRCAPriorTest extends TestCase {
         prior.initByName("tree", tree, "taxonset", set, "monophyletic", true);
         logP = prior.calculateLogP();
         assertEquals(logP, Double.NEGATIVE_INFINITY, 0);
+
+    
+        set.setID("test");
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(baos);
+        prior.init(ps);
+        String log = new String(baos.toByteArray(), StandardCharsets.UTF_8);
+        assertEquals(log, "mrcatime(test)\t");
+
+        baos = new ByteArrayOutputStream();
+        ps = new PrintStream(baos);
+        prior.initByName("tree", tree, "taxonset", set, "monophyletic", true, "useOriginate", true);
+        prior.init(ps);
+        log = new String(baos.toByteArray(), StandardCharsets.UTF_8);
+        assertEquals(log, "mrcatime(test.originate)\t");
     }
 
     @Test
diff --git a/src/test/beast/math/distributions/NormalDistributionTest.java b/src/test/beast/math/distributions/NormalDistributionTest.java
new file mode 100644
index 0000000..f88b46c
--- /dev/null
+++ b/src/test/beast/math/distributions/NormalDistributionTest.java
@@ -0,0 +1,132 @@
+package test.beast.math.distributions;
+
+import org.apache.commons.math.MathException;
+
+import beast.math.distributions.Normal;
+import beast.util.Randomizer;
+import junit.framework.TestCase;
+
+/**
+ * adapted from BEAST 1
+ * @author Wai Lok Sibon Li
+ * 
+ */
+public class NormalDistributionTest extends TestCase {
+    Normal norm;
+
+    public void setUp() {
+
+        norm = new Normal();
+        norm.initAndValidate();
+        Randomizer.setSeed(123);
+    }
+
+
+    public void testPdf() {
+
+        System.out.println("Testing 10000 random pdf calls");
+
+        for (int i = 0; i < 10000; i++) {
+            double M = Randomizer.nextDouble() * 10.0 - 5.0;
+            double S = Randomizer.nextDouble() * 10;
+
+            double x = Randomizer.nextDouble() * 10;
+
+            norm.meanInput.setValue(M + "", norm);
+            norm.sigmaInput.setValue(S + "", norm);
+            norm.initAndValidate();
+            
+            double a = 1.0 / (Math.sqrt(2.0 * Math.PI) * S);
+            double b = -(x - M) * (x - M) / (2.0 * S * S);
+            double pdf =  a * Math.exp(b);
+
+            assertEquals(pdf, norm.density(x), 1e-10);
+        }
+
+        /* Test with an example using R */
+        norm.meanInput.setValue(2.835202292812448 + "", norm);
+        norm.sigmaInput.setValue(3.539139491639669 + "", norm);
+        assertEquals(0.1123318, norm.density(2.540111), 1e-6);
+    }
+
+    public void testMean() {
+
+        for (int i = 0; i < 1000; i++) {
+            double M = Randomizer.nextDouble() * 10.0 - 5.0;
+
+            norm.meanInput.setValue(M + "", norm);
+            norm.initAndValidate();
+
+            assertEquals(M, norm.getMean(), 1e-10);
+        }
+    }
+
+//    public void testVariance() {
+//
+//        for (int i = 0; i < 1000; i++) {
+//            double S = Randomizer.nextDouble() * 10;
+//            norm.sigmaInput.setValue(S + "", norm);
+//            norm.initAndValidate();
+//
+//            double variance = S * S;
+//
+//            assertEquals(variance, norm.variance(), 1e-10);
+//        }
+//    }
+
+
+    public void testMedian() throws MathException {
+
+        System.out.println("Testing 10000 random quantile(0.5) calls");
+
+        for (int i = 0; i < 10000; i++) {
+            double M = Randomizer.nextDouble() * 10.0 - 5.0;
+            double S = Randomizer.nextDouble() * 10;
+
+            norm.meanInput.setValue(M + "", norm);
+            norm.sigmaInput.setValue(S + "", norm);
+            norm.initAndValidate();
+
+            double median = M;
+
+            assertEquals(median, norm.inverseCumulativeProbability(0.5), 1e-6);
+        }
+    }
+
+    public void testCDFAndQuantile() throws MathException {
+
+        System.out.println("Testing 10000 random quantile/cdf pairs");
+
+        for (int i = 0; i < 10000; i++) {
+
+            double M = Randomizer.nextDouble() * 10.0 - 5.0;
+            double S = Randomizer.nextDouble() * 10;
+
+            norm.meanInput.setValue(M + "", norm);
+            norm.sigmaInput.setValue(S + "", norm);
+            norm.initAndValidate();
+
+            double p = Randomizer.nextDouble();
+            double quantile = norm.inverseCumulativeProbability(p);
+            double cdf = norm.cumulativeProbability(quantile);
+
+            assertEquals(p, cdf, 1e-7);
+        }
+
+    }
+
+//    public void testCDFAndQuantile2() {
+//        for(int i=0; i<10000; i++) {
+//            double x =Randomizer.nextDouble();
+//            double m = Randomizer.nextDouble() * 10;
+//            double s = Randomizer.nextDouble() * 10;
+//            
+//            double a = NormalDistribution.cdf(x, m, s, false);
+//            double b =NormalDistribution.cdf(x, m, s);
+//            
+//            assertEquals(a, b, 1.0e-8);
+//        }
+//    }
+
+
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/beast2-mcmc.git



More information about the debian-med-commit mailing list